mirror of
https://github.com/nextui-org/nextui.git
synced 2025-12-08 19:26:11 +00:00
feat(accordion): stories added
This commit is contained in:
parent
a2c16c0561
commit
a9132c9df3
@ -57,6 +57,7 @@
|
||||
"framer-motion": "^10.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nextui-org/avatar": "workspace:*",
|
||||
"@react-types/accordion": "3.0.0-alpha.12",
|
||||
"@react-types/shared": "^3.17.0",
|
||||
"clean-package": "2.2.0",
|
||||
|
||||
@ -11,6 +11,8 @@ const Accordion = forwardRef<AccordionItemProps, "div">((props, ref) => {
|
||||
const {
|
||||
Component,
|
||||
item,
|
||||
styles,
|
||||
slots,
|
||||
isOpen,
|
||||
isDisabled,
|
||||
disableAnimation,
|
||||
@ -29,6 +31,8 @@ const Accordion = forwardRef<AccordionItemProps, "div">((props, ref) => {
|
||||
return item.props?.indicator({indicator: <ChevronIcon />, isOpen, isDisabled});
|
||||
}
|
||||
|
||||
if (item.props?.indicator) return item.props?.indicator;
|
||||
|
||||
return <ChevronIcon />;
|
||||
}, [item.props?.indicator, isOpen, isDisabled]);
|
||||
|
||||
@ -48,8 +52,15 @@ const Accordion = forwardRef<AccordionItemProps, "div">((props, ref) => {
|
||||
<Component {...getBaseProps()}>
|
||||
<h2 {...getHeadingProps()}>
|
||||
<button {...getButtonProps()}>
|
||||
{item.props?.title && <h3 {...getTitleProps()}>{item.props?.title}</h3>}
|
||||
{item.props?.subtitle && <p {...getSubtitleProps()}>{item.props?.subtitle}</p>}
|
||||
{item.props?.leftIndicator && (
|
||||
<div className={slots.leftIndicator({class: styles?.leftIndicator})}>
|
||||
{item.props?.leftIndicator}
|
||||
</div>
|
||||
)}
|
||||
<div className={slots.titleWrapper({class: styles?.titleWrapper})}>
|
||||
{item.props?.title && <span {...getTitleProps()}>{item.props?.title}</span>}
|
||||
{item.props?.subtitle && <span {...getSubtitleProps()}>{item.props?.subtitle}</span>}
|
||||
</div>
|
||||
{indicator && <span {...getIndicatorProps()}>{indicator}</span>}
|
||||
</button>
|
||||
</h2>
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
import type {AccordionItemVariantProps} from "@nextui-org/theme";
|
||||
import type {
|
||||
AccordionItemVariantProps,
|
||||
AccordionItemSlots,
|
||||
SlotsToClasses,
|
||||
} from "@nextui-org/theme";
|
||||
import type {CollapseProps} from "@nextui-org/framer-transitions";
|
||||
|
||||
import {BaseItem, ItemProps} from "@nextui-org/aria-utils";
|
||||
@ -38,10 +42,34 @@ export interface Props<T extends object = {}>
|
||||
* In case you want to use a custom indicator or muodify the current one.
|
||||
*/
|
||||
indicator?: ReactNode | ((props: AccordionItemIndicatorProps) => ReactNode) | null;
|
||||
/**
|
||||
* The accordion item left indicator, it's usually an icon or avatar.
|
||||
*/
|
||||
leftIndicator?: ReactNode;
|
||||
/**
|
||||
* The properties passed to the underlying `Collapse` component.
|
||||
*/
|
||||
motionProps?: CollapseProps;
|
||||
/**
|
||||
* Classname or List of classes to change the styles of the element.
|
||||
* if `className` is passed, it will be added to the base slot.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* <AccordionItem styles={{
|
||||
* base:"base-classes",
|
||||
* heading: "heading-classes",
|
||||
* trigger: "trigger-classes",
|
||||
* leftIndicator: "left-indicator-classes",
|
||||
* indicator: "indicator-classes",
|
||||
* titleWrapper: "title-wrapper-classes", // this wraps the title and subtitle
|
||||
* title: "title-classes",
|
||||
* subtitle: "subtitle-classes",
|
||||
* content: "content-classes",
|
||||
* }} />
|
||||
* ```
|
||||
*/
|
||||
styles?: SlotsToClasses<AccordionItemSlots>;
|
||||
}
|
||||
|
||||
export type AccordionItemBaseProps<T extends object = {}> = Props<T> & AccordionItemVariantProps;
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
import type {AccordionItemSlots, SlotsToClasses} from "@nextui-org/theme";
|
||||
|
||||
import {HTMLNextUIProps, PropGetter} from "@nextui-org/system";
|
||||
import {useFocusRing} from "@react-aria/focus";
|
||||
import {accordionItem} from "@nextui-org/theme";
|
||||
@ -21,22 +19,6 @@ export interface Props<T extends object> extends HTMLNextUIProps<"div"> {
|
||||
* The item node.
|
||||
*/
|
||||
item: NodeWithProps<T, AccordionItemBaseProps<T>>;
|
||||
/**
|
||||
* Classname or List of classes to change the styles of the element.
|
||||
* if `className` is passed, it will be added to the base slot.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* <AccordionItem styles={{
|
||||
* base:"base-classes",
|
||||
* heading: "heading-classes",
|
||||
* indicator: "indicator-classes",
|
||||
* trigger: "trigger-classes",
|
||||
* content: "content-classes",
|
||||
* }} />
|
||||
* ```
|
||||
*/
|
||||
styles?: SlotsToClasses<AccordionItemSlots>;
|
||||
}
|
||||
|
||||
export type UseAccordionItemProps<T extends object = {}> = Props<T> & AccordionItemBaseProps;
|
||||
@ -47,33 +29,45 @@ export function useAccordionItem<T extends object = {}>(props: UseAccordionItemP
|
||||
const {
|
||||
ref,
|
||||
as,
|
||||
styles,
|
||||
className,
|
||||
item,
|
||||
size = extractProperty(
|
||||
"size",
|
||||
"md",
|
||||
styles = item.props?.styles,
|
||||
className = item.props?.className,
|
||||
isCompact = extractProperty(
|
||||
"isCompact",
|
||||
false,
|
||||
groupContext,
|
||||
item.props,
|
||||
) as AccordionItemBaseProps["size"],
|
||||
radius = extractProperty(
|
||||
"radius",
|
||||
"lg",
|
||||
groupContext,
|
||||
item.props,
|
||||
) as AccordionItemBaseProps["radius"],
|
||||
) as AccordionItemBaseProps["isCompact"],
|
||||
isDisabled: isDisabledProp = extractProperty(
|
||||
"isDisabled",
|
||||
false,
|
||||
groupContext,
|
||||
item.props,
|
||||
) as AccordionItemBaseProps["isDisabled"],
|
||||
hideDivider = extractProperty(
|
||||
"hideDivider",
|
||||
false,
|
||||
groupContext,
|
||||
item.props,
|
||||
) as AccordionItemBaseProps["hideDivider"],
|
||||
hideIndicator = extractProperty(
|
||||
"hideIndicator",
|
||||
false,
|
||||
groupContext,
|
||||
item.props,
|
||||
) as AccordionItemBaseProps["hideIndicator"],
|
||||
disableAnimation = extractProperty(
|
||||
"disableAnimation",
|
||||
false,
|
||||
groupContext,
|
||||
item.props,
|
||||
) as AccordionItemBaseProps["disableAnimation"],
|
||||
disableIndicatorAnimation = extractProperty(
|
||||
"disableIndicatorAnimation",
|
||||
false,
|
||||
groupContext,
|
||||
item.props,
|
||||
) as AccordionItemBaseProps["disableIndicatorAnimation"],
|
||||
onFocusChange,
|
||||
motionProps = item.props?.motionProps ?? groupContext?.motionProps,
|
||||
...otherProps
|
||||
@ -110,13 +104,23 @@ export function useAccordionItem<T extends object = {}>(props: UseAccordionItemP
|
||||
const slots = useMemo(
|
||||
() =>
|
||||
accordionItem({
|
||||
size,
|
||||
radius,
|
||||
isCompact,
|
||||
isDisabled,
|
||||
hideDivider,
|
||||
hideIndicator,
|
||||
isFocusVisible,
|
||||
disableAnimation,
|
||||
disableIndicatorAnimation,
|
||||
}),
|
||||
[size, radius, isDisabled, isFocusVisible, disableAnimation],
|
||||
[
|
||||
isCompact,
|
||||
isDisabled,
|
||||
hideDivider,
|
||||
hideIndicator,
|
||||
isFocusVisible,
|
||||
disableAnimation,
|
||||
disableIndicatorAnimation,
|
||||
],
|
||||
);
|
||||
|
||||
const baseStyles = clsx(styles?.base, className);
|
||||
@ -125,7 +129,6 @@ export function useAccordionItem<T extends object = {}>(props: UseAccordionItemP
|
||||
(props = {}) => {
|
||||
return {
|
||||
className: slots.base({class: baseStyles}),
|
||||
|
||||
...mergeProps(otherProps, props),
|
||||
};
|
||||
},
|
||||
|
||||
@ -2,6 +2,7 @@ import type {HTMLNextUIProps, PropGetter} from "@nextui-org/system";
|
||||
import type {ReactRef} from "@nextui-org/shared-utils";
|
||||
import type {SelectionBehavior, MultipleSelection} from "@react-types/shared";
|
||||
import type {AriaAccordionProps} from "@react-types/accordion";
|
||||
import type {AccordionGroupVariantProps} from "@nextui-org/theme";
|
||||
|
||||
import {Key, useCallback} from "react";
|
||||
import {TreeState, useTreeState} from "@react-stately/tree";
|
||||
@ -26,17 +27,29 @@ interface Props extends HTMLNextUIProps<"div"> {
|
||||
}
|
||||
|
||||
export type UseAccordionProps<T extends object = {}> = Props &
|
||||
Pick<AccordionItemProps, "size" | "radius" | "isDisabled" | "disableAnimation" | "motionProps"> &
|
||||
AccordionGroupVariantProps &
|
||||
Pick<
|
||||
AccordionItemProps,
|
||||
| "isCompact"
|
||||
| "isDisabled"
|
||||
| "hideDivider"
|
||||
| "hideIndicator"
|
||||
| "disableAnimation"
|
||||
| "disableIndicatorAnimation"
|
||||
| "motionProps"
|
||||
> &
|
||||
AriaAccordionProps<T> &
|
||||
MultipleSelection;
|
||||
|
||||
export type ContextType<T extends object = {}> = {
|
||||
state: TreeState<T>;
|
||||
focusedKey?: Key | null;
|
||||
size?: AccordionItemProps["size"];
|
||||
radius?: AccordionItemProps["radius"];
|
||||
isCompact?: AccordionItemProps["isCompact"];
|
||||
isDisabled?: AccordionItemProps["isDisabled"];
|
||||
hideDivider?: AccordionItemProps["hideDivider"];
|
||||
hideIndicator?: AccordionItemProps["hideIndicator"];
|
||||
disableAnimation?: AccordionItemProps["disableAnimation"];
|
||||
disableIndicatorAnimation?: AccordionItemProps["disableAnimation"];
|
||||
motionProps?: AccordionItemProps["motionProps"];
|
||||
};
|
||||
|
||||
@ -47,21 +60,25 @@ export function useAccordion<T extends object>(props: UseAccordionProps<T>) {
|
||||
children,
|
||||
className,
|
||||
items,
|
||||
variant,
|
||||
motionProps,
|
||||
expandedKeys,
|
||||
defaultExpandedKeys,
|
||||
disabledKeys,
|
||||
selectedKeys,
|
||||
defaultExpandedKeys,
|
||||
selectionMode = "single",
|
||||
selectionBehavior = "toggle",
|
||||
disallowEmptySelection,
|
||||
defaultSelectedKeys,
|
||||
onExpandedChange,
|
||||
onSelectionChange,
|
||||
size = "md",
|
||||
radius = "lg",
|
||||
isCompact = false,
|
||||
isDisabled = false,
|
||||
hideDivider = false,
|
||||
hideIndicator = false,
|
||||
disableAnimation = false,
|
||||
motionProps,
|
||||
disableIndicatorAnimation = false,
|
||||
|
||||
...otherProps
|
||||
} = props;
|
||||
|
||||
@ -74,9 +91,10 @@ export function useAccordion<T extends object>(props: UseAccordionProps<T>) {
|
||||
const styles = useMemo(
|
||||
() =>
|
||||
accordion({
|
||||
variant,
|
||||
className,
|
||||
}),
|
||||
[className],
|
||||
[variant, className],
|
||||
);
|
||||
|
||||
const commonProps = {
|
||||
@ -119,15 +137,27 @@ export function useAccordion<T extends object>(props: UseAccordionProps<T>) {
|
||||
|
||||
const context: ContextType<T> = useMemo(
|
||||
() => ({
|
||||
size,
|
||||
radius,
|
||||
focusedKey,
|
||||
state,
|
||||
motionProps,
|
||||
isCompact,
|
||||
isDisabled,
|
||||
hideDivider,
|
||||
hideIndicator,
|
||||
disableAnimation,
|
||||
disableIndicatorAnimation,
|
||||
}),
|
||||
[size, radius, state, focusedKey, isDisabled, disableAnimation, motionProps],
|
||||
[
|
||||
state,
|
||||
focusedKey,
|
||||
isCompact,
|
||||
isDisabled,
|
||||
hideIndicator,
|
||||
hideDivider,
|
||||
disableAnimation,
|
||||
disableIndicatorAnimation,
|
||||
motionProps,
|
||||
],
|
||||
);
|
||||
|
||||
const getBaseProps: PropGetter = useCallback((props = {}) => {
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import React from "react";
|
||||
import {ComponentStory, ComponentMeta} from "@storybook/react";
|
||||
import {accordionItem} from "@nextui-org/theme";
|
||||
import {accordionItem, link} from "@nextui-org/theme";
|
||||
import {AnchorIcon, MoonIcon, SunIcon} from "@nextui-org/shared-icons";
|
||||
import {Avatar} from "@nextui-org/avatar";
|
||||
|
||||
import {Accordion, AccordionProps, AccordionItem} from "../src";
|
||||
|
||||
@ -8,22 +10,10 @@ export default {
|
||||
title: "Components/Accordion",
|
||||
component: Accordion,
|
||||
argTypes: {
|
||||
color: {
|
||||
variant: {
|
||||
control: {
|
||||
type: "select",
|
||||
options: ["neutral", "primary", "secondary", "success", "warning", "danger"],
|
||||
},
|
||||
},
|
||||
radius: {
|
||||
control: {
|
||||
type: "select",
|
||||
options: ["none", "base", "sm", "md", "lg", "xl", "full"],
|
||||
},
|
||||
},
|
||||
size: {
|
||||
control: {
|
||||
type: "select",
|
||||
options: ["xs", "sm", "md", "lg", "xl"],
|
||||
options: ["default", "shadow", "bordered", "splitted"],
|
||||
},
|
||||
},
|
||||
isDisabled: {
|
||||
@ -44,22 +34,173 @@ const defaultProps = {
|
||||
selectionMode: "single",
|
||||
};
|
||||
|
||||
const defaultContent =
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.";
|
||||
|
||||
const Template: ComponentStory<typeof Accordion> = (args: AccordionProps) => (
|
||||
<Accordion {...args}>
|
||||
<AccordionItem key="1" title="Accordion 1">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||
labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
|
||||
laboris nisi ut aliquip ex ea commodo consequat.
|
||||
{defaultContent}
|
||||
</AccordionItem>
|
||||
<AccordionItem key="2" title="Accordion 2">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||
labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
|
||||
laboris nisi ut aliquip ex ea commodo consequat.
|
||||
{defaultContent}
|
||||
</AccordionItem>
|
||||
<AccordionItem key="3" title="Accordion 3">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
||||
labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
|
||||
laboris nisi ut aliquip ex ea commodo consequat.
|
||||
{defaultContent}
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
);
|
||||
|
||||
const TemplateWithSubtitle: ComponentStory<typeof Accordion> = (args: AccordionProps) => (
|
||||
<Accordion {...args}>
|
||||
<AccordionItem key="1" subtitle="Press to expand" title="Accordion 1">
|
||||
{defaultContent}
|
||||
</AccordionItem>
|
||||
<AccordionItem
|
||||
key="2"
|
||||
subtitle={
|
||||
<span>
|
||||
Press to expand <strong>key 2</strong>
|
||||
</span>
|
||||
}
|
||||
title="Accordion 2"
|
||||
>
|
||||
{defaultContent}
|
||||
</AccordionItem>
|
||||
<AccordionItem key="3" subtitle="Press to expand" title="Accordion 3">
|
||||
{defaultContent}
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
);
|
||||
|
||||
const TemplateWithLeftIndicator: ComponentStory<typeof Accordion> = (args: AccordionProps) => (
|
||||
<Accordion {...args} variant="shadow">
|
||||
<AccordionItem
|
||||
key="1"
|
||||
leftIndicator={
|
||||
<Avatar
|
||||
isBordered
|
||||
color="primary"
|
||||
radius="xl"
|
||||
src="https://i.pravatar.cc/150?u=a042581f4e29026024d"
|
||||
/>
|
||||
}
|
||||
subtitle="4 unread messages"
|
||||
title="Chung Miller"
|
||||
>
|
||||
{defaultContent}
|
||||
</AccordionItem>
|
||||
<AccordionItem
|
||||
key="2"
|
||||
leftIndicator={
|
||||
<Avatar
|
||||
isBordered
|
||||
color="success"
|
||||
radius="xl"
|
||||
src="https://i.pravatar.cc/150?u=a042581f4e29026704d"
|
||||
/>
|
||||
}
|
||||
subtitle="3 incompleted steps"
|
||||
title="Janelle Lenard"
|
||||
>
|
||||
{defaultContent}
|
||||
</AccordionItem>
|
||||
<AccordionItem
|
||||
key="3"
|
||||
leftIndicator={
|
||||
<Avatar
|
||||
isBordered
|
||||
color="warning"
|
||||
radius="xl"
|
||||
src="https://i.pravatar.cc/150?u=a04258114e29026702d"
|
||||
/>
|
||||
}
|
||||
subtitle={
|
||||
<p>
|
||||
2 issues to{" "}
|
||||
<a className={link()} href="/?path=/story/components-accordion--with-left-indicator">
|
||||
fix now
|
||||
</a>
|
||||
</p>
|
||||
}
|
||||
title="Zoey Lang"
|
||||
>
|
||||
{defaultContent}
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
);
|
||||
|
||||
const VariantsTemplate: ComponentStory<typeof Accordion> = (args: AccordionProps) => (
|
||||
<div className="flex flex-col gap-8 mb-24">
|
||||
<div className="flex flex-col gap-4">
|
||||
<h3>Default</h3>
|
||||
<Accordion {...args}>
|
||||
<AccordionItem key="1" title="Accordion 1">
|
||||
{defaultContent}
|
||||
</AccordionItem>
|
||||
<AccordionItem key="2" title="Accordion 2">
|
||||
{defaultContent}
|
||||
</AccordionItem>
|
||||
<AccordionItem key="3" title="Accordion 3">
|
||||
{defaultContent}
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
<h3>Shadow</h3>
|
||||
<Accordion {...args} variant="shadow">
|
||||
<AccordionItem key="1" title="Accordion 1">
|
||||
{defaultContent}
|
||||
</AccordionItem>
|
||||
<AccordionItem key="2" title="Accordion 2">
|
||||
{defaultContent}
|
||||
</AccordionItem>
|
||||
<AccordionItem key="3" title="Accordion 3">
|
||||
{defaultContent}
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
<h3>Bordered</h3>
|
||||
<Accordion {...args} variant="bordered">
|
||||
<AccordionItem key="1" title="Accordion 1">
|
||||
{defaultContent}
|
||||
</AccordionItem>
|
||||
<AccordionItem key="2" title="Accordion 2">
|
||||
{defaultContent}
|
||||
</AccordionItem>
|
||||
<AccordionItem key="3" title="Accordion 3">
|
||||
{defaultContent}
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
<h3>Splitted</h3>
|
||||
<Accordion {...args} variant="splitted">
|
||||
<AccordionItem key="1" title="Accordion 1">
|
||||
{defaultContent}
|
||||
</AccordionItem>
|
||||
<AccordionItem key="2" title="Accordion 2">
|
||||
{defaultContent}
|
||||
</AccordionItem>
|
||||
<AccordionItem key="3" title="Accordion 3">
|
||||
{defaultContent}
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const CustomInidicatorTemplate: ComponentStory<typeof Accordion> = (args: AccordionProps) => (
|
||||
<Accordion {...args}>
|
||||
<AccordionItem key="anchor" indicator={<AnchorIcon />} title="Anchor">
|
||||
{defaultContent}
|
||||
</AccordionItem>
|
||||
<AccordionItem key="moon" indicator={<MoonIcon />} title="Moon">
|
||||
{defaultContent}
|
||||
</AccordionItem>
|
||||
<AccordionItem key="sun" indicator={<SunIcon />} title="Sun">
|
||||
{defaultContent}
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
);
|
||||
@ -68,3 +209,65 @@ export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
...defaultProps,
|
||||
};
|
||||
|
||||
export const IsCompact = Template.bind({});
|
||||
IsCompact.args = {
|
||||
...defaultProps,
|
||||
isCompact: true,
|
||||
};
|
||||
|
||||
export const Multiple = Template.bind({});
|
||||
Multiple.args = {
|
||||
...defaultProps,
|
||||
selectionMode: "multiple",
|
||||
};
|
||||
|
||||
export const DefaultExpanded = Template.bind({});
|
||||
DefaultExpanded.args = {
|
||||
...defaultProps,
|
||||
defaultExpandedKeys: ["2"],
|
||||
};
|
||||
|
||||
export const DisabledKeys = Template.bind({});
|
||||
DisabledKeys.args = {
|
||||
...defaultProps,
|
||||
disabledKeys: ["2"],
|
||||
};
|
||||
|
||||
export const WithSubtitle = TemplateWithSubtitle.bind({});
|
||||
WithSubtitle.args = {
|
||||
...defaultProps,
|
||||
};
|
||||
|
||||
export const WithLeftIndicator = TemplateWithLeftIndicator.bind({});
|
||||
WithLeftIndicator.args = {
|
||||
...defaultProps,
|
||||
};
|
||||
|
||||
export const Variants = VariantsTemplate.bind({});
|
||||
Variants.args = {
|
||||
...defaultProps,
|
||||
};
|
||||
|
||||
export const CustomMotion = Template.bind({});
|
||||
CustomMotion.args = {
|
||||
...defaultProps,
|
||||
motionProps: {
|
||||
startingY: 0,
|
||||
transition: {
|
||||
exit: {
|
||||
height: {duration: 0.2},
|
||||
opacity: {duration: 0.3},
|
||||
},
|
||||
enter: {
|
||||
height: {duration: 0.3},
|
||||
opacity: {duration: 0.6},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const CustomIndicator = CustomInidicatorTemplate.bind({});
|
||||
CustomIndicator.args = {
|
||||
...defaultProps,
|
||||
};
|
||||
|
||||
@ -4,45 +4,57 @@ import {ringClasses} from "../utils";
|
||||
/**
|
||||
* AccordionItem wrapper **Tailwind Variants** component
|
||||
*
|
||||
* const {base, heading, indicator, trigger, content } = accordionItem({...})
|
||||
* const {base, heading, indicator, trigger, leftIndicator, title, subtitle, content } = accordionItem({...})
|
||||
*
|
||||
* @example
|
||||
* <div className={base())}>
|
||||
* <div className={heading())}>
|
||||
* <button className={trigger())}>Trigger</button>
|
||||
* <span className={indicator())}>Indicator</span>
|
||||
* <button className={trigger())}>
|
||||
* <div className={leftIndicator()}>
|
||||
* // content
|
||||
* </div>
|
||||
* <div className={titleWrapper()}>
|
||||
* <h3 className={title())}>Title</h3>
|
||||
* <span className={subtitle())}>Subtitle</span>
|
||||
* </div>
|
||||
* <span className={indicator())}>Indicator</span>
|
||||
* </button>
|
||||
* </div>
|
||||
* <div className={content())}>Content</div>
|
||||
* </div>
|
||||
*/
|
||||
const accordionItem = tv({
|
||||
slots: {
|
||||
base: "py-2 [&:not(:last-of-type)]:border-b border-neutral",
|
||||
base: [
|
||||
"py-2",
|
||||
"border-neutral",
|
||||
"[&:not(:last-of-type)]:border-b",
|
||||
"group-[.is-splitted]:px-4",
|
||||
"dark:group-[.is-splitted]:bg-content1",
|
||||
"group-[.is-splitted]:shadow-lg",
|
||||
"group-[.is-splitted]:rounded-lg",
|
||||
"group-[.is-splitted]:border",
|
||||
"group-[.is-splitted]:border-neutral-100",
|
||||
],
|
||||
heading: "",
|
||||
trigger: "py-2 flex w-full outline-none items-center",
|
||||
indicator: "rotate-0 data-[open=true]:-rotate-90",
|
||||
title: "flex-1 text-left text-foreground",
|
||||
subtitle: "",
|
||||
trigger: "py-2 flex w-full gap-3 outline-none items-center",
|
||||
leftIndicator: "flex-shrink-0",
|
||||
indicator: "text-neutral-400",
|
||||
titleWrapper: "flex-1 flex flex-col text-left",
|
||||
title: "text-foreground text-lg",
|
||||
subtitle: "text-sm text-neutral-500 font-normal",
|
||||
content: "py-2",
|
||||
},
|
||||
variants: {
|
||||
size: {
|
||||
xs: {},
|
||||
sm: {},
|
||||
md: {},
|
||||
lg: {},
|
||||
xl: {},
|
||||
},
|
||||
radius: {
|
||||
none: {},
|
||||
base: {},
|
||||
sm: {},
|
||||
md: {},
|
||||
lg: {},
|
||||
xl: {},
|
||||
"2xl": {},
|
||||
"3xl": {},
|
||||
full: {},
|
||||
isCompact: {
|
||||
true: {
|
||||
base: "py-1",
|
||||
trigger: "py-1",
|
||||
title: "text-base",
|
||||
subtitle: "text-xs",
|
||||
indicator: "text-base",
|
||||
content: "py-1",
|
||||
},
|
||||
},
|
||||
isDisabled: {
|
||||
true: {
|
||||
@ -54,6 +66,16 @@ const accordionItem = tv({
|
||||
trigger: [...ringClasses],
|
||||
},
|
||||
},
|
||||
hideDivider: {
|
||||
true: {
|
||||
base: "!border-b-0",
|
||||
},
|
||||
},
|
||||
hideIndicator: {
|
||||
true: {
|
||||
indicator: "hidden",
|
||||
},
|
||||
},
|
||||
disableAnimation: {
|
||||
true: {
|
||||
content: "hidden data-[open=true]:block",
|
||||
@ -62,12 +84,23 @@ const accordionItem = tv({
|
||||
indicator: "transition-transform",
|
||||
},
|
||||
},
|
||||
disableIndicatorAnimation: {
|
||||
true: {
|
||||
indicator: "transition-none",
|
||||
},
|
||||
false: {
|
||||
indicator: "rotate-0 data-[open=true]:-rotate-90",
|
||||
},
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
size: "md",
|
||||
radius: "lg",
|
||||
isDisabled: false,
|
||||
hideDivider: false,
|
||||
hideIndicator: false,
|
||||
disableAnimation: false,
|
||||
disableIndicatorAnimation: false,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@ -12,7 +12,13 @@ import {tv, VariantProps} from "tailwind-variants";
|
||||
*/
|
||||
const accordion = tv({
|
||||
base: "px-2",
|
||||
variants: {},
|
||||
variants: {
|
||||
variant: {
|
||||
shadow: "px-4 shadow-lg rounded-lg dark:bg-content1 border border-neutral-100",
|
||||
bordered: "px-4 border border-neutral rounded-lg",
|
||||
splitted: "group is-splitted flex flex-col gap-2", // the styles are applied in the accordion-item component
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export type AccordionGroupVariantProps = VariantProps<typeof accordion>;
|
||||
|
||||
@ -6,7 +6,7 @@ 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, withDelay, WithTransitionConfig} from "./transition-utils";
|
||||
import {TRANSITION_EASINGS, Variants, WithTransitionConfig, withDelay} from "./transition-utils";
|
||||
|
||||
export interface CollapseOptions {
|
||||
/**
|
||||
@ -19,21 +19,21 @@ export interface CollapseOptions {
|
||||
* @default 0
|
||||
*/
|
||||
startingHeight?: number;
|
||||
/**
|
||||
* The y position you want the content in its collapsed state.
|
||||
* @default -16
|
||||
*/
|
||||
startingY?: number;
|
||||
/**
|
||||
* The y position you want the content in its expanded state.
|
||||
* @default 0
|
||||
*/
|
||||
endingY?: 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 = {
|
||||
@ -46,37 +46,25 @@ const defaultTransitions = {
|
||||
duration: 0.3,
|
||||
ease: TRANSITION_EASINGS.ease,
|
||||
},
|
||||
y: {
|
||||
duration: 0.3,
|
||||
ease: TRANSITION_EASINGS.ease,
|
||||
},
|
||||
},
|
||||
enter: {
|
||||
height: {
|
||||
duration: 0.3,
|
||||
ease: TRANSITION_EASINGS.ease,
|
||||
type: "spring",
|
||||
stiffness: 300,
|
||||
damping: 24,
|
||||
},
|
||||
opacity: {
|
||||
duration: 0.4,
|
||||
duration: 0.8,
|
||||
ease: TRANSITION_EASINGS.ease,
|
||||
delay: 0.05,
|
||||
},
|
||||
y: {
|
||||
duration: 0.4,
|
||||
duration: 0.5,
|
||||
ease: TRANSITION_EASINGS.ease,
|
||||
delay: 0.03,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const variants: Variants<CollapseOptions> = {
|
||||
exit: ({animateOpacity, startingHeight, startingY, transition, transitionEnd, delay}) => ({
|
||||
...(animateOpacity && {opacity: isNumeric(startingHeight) ? 1 : 0}),
|
||||
y: startingY,
|
||||
height: startingHeight,
|
||||
transitionEnd: transitionEnd?.exit,
|
||||
transition: transition?.exit ?? withDelay.exit(defaultTransitions.exit, delay),
|
||||
}),
|
||||
enter: ({animateOpacity, endingHeight, endingY, transition, transitionEnd, delay}) => ({
|
||||
...(animateOpacity && {opacity: 1}),
|
||||
y: endingY,
|
||||
@ -84,6 +72,13 @@ const variants: Variants<CollapseOptions> = {
|
||||
transitionEnd: transitionEnd?.enter,
|
||||
transition: transition?.enter ?? withDelay.enter(defaultTransitions.enter, delay),
|
||||
}),
|
||||
exit: ({animateOpacity, startingHeight, transition, startingY, transitionEnd, delay}) => ({
|
||||
...(animateOpacity && {opacity: isNumeric(startingHeight) ? 1 : 0}),
|
||||
y: startingY,
|
||||
height: startingHeight,
|
||||
transitionEnd: transitionEnd?.exit,
|
||||
transition: transition?.exit ?? withDelay.exit(defaultTransitions.exit, delay),
|
||||
}),
|
||||
};
|
||||
|
||||
export type ICollapse = CollapseProps;
|
||||
@ -99,7 +94,7 @@ export const Collapse = forwardRef<HTMLDivElement, CollapseProps>((props, ref) =
|
||||
animateOpacity = true,
|
||||
startingHeight = 0,
|
||||
endingHeight = "auto",
|
||||
startingY = -16,
|
||||
startingY = 10,
|
||||
endingY = 0,
|
||||
style,
|
||||
className,
|
||||
|
||||
22
packages/utilities/shared-icons/src/anchor.tsx
Normal file
22
packages/utilities/shared-icons/src/anchor.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import {IconSvgProps} from "./types";
|
||||
|
||||
export const AnchorIcon = (props: IconSvgProps) => (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
role="presentation"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M8.465,11.293c1.133-1.133,3.109-1.133,4.242,0L13.414,12l1.414-1.414l-0.707-0.707c-0.943-0.944-2.199-1.465-3.535-1.465 S7.994,8.935,7.051,9.879L4.929,12c-1.948,1.949-1.948,5.122,0,7.071c0.975,0.975,2.255,1.462,3.535,1.462 c1.281,0,2.562-0.487,3.536-1.462l0.707-0.707l-1.414-1.414l-0.707,0.707c-1.17,1.167-3.073,1.169-4.243,0 c-1.169-1.17-1.169-3.073,0-4.243L8.465,11.293z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M12,4.929l-0.707,0.707l1.414,1.414l0.707-0.707c1.169-1.167,3.072-1.169,4.243,0c1.169,1.17,1.169,3.073,0,4.243 l-2.122,2.121c-1.133,1.133-3.109,1.133-4.242,0L10.586,12l-1.414,1.414l0.707,0.707c0.943,0.944,2.199,1.465,3.535,1.465 s2.592-0.521,3.535-1.465L19.071,12c1.948-1.949,1.948-5.122,0-7.071C17.121,2.979,13.948,2.98,12,4.929z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
@ -13,3 +13,4 @@ export * from "./mail";
|
||||
export * from "./moon";
|
||||
export * from "./moon-filled";
|
||||
export * from "./headphones";
|
||||
export * from "./anchor";
|
||||
|
||||
2
pnpm-lock.yaml
generated
2
pnpm-lock.yaml
generated
@ -306,6 +306,7 @@ importers:
|
||||
packages/components/accordion:
|
||||
specifiers:
|
||||
'@nextui-org/aria-utils': workspace:*
|
||||
'@nextui-org/avatar': workspace:*
|
||||
'@nextui-org/dom-utils': workspace:*
|
||||
'@nextui-org/framer-transitions': workspace:*
|
||||
'@nextui-org/shared-icons': workspace:*
|
||||
@ -335,6 +336,7 @@ importers:
|
||||
'@react-stately/tree': 3.5.0_react@18.2.0
|
||||
framer-motion: 10.6.0_react@18.2.0
|
||||
devDependencies:
|
||||
'@nextui-org/avatar': link:../avatar
|
||||
'@react-types/accordion': 3.0.0-alpha.12_react@18.2.0
|
||||
'@react-types/shared': 3.17.0_react@18.2.0
|
||||
clean-package: 2.2.0
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user