mirror of
https://github.com/nextui-org/nextui.git
synced 2025-12-08 19:26:11 +00:00
feat(progress): circular progress added
This commit is contained in:
parent
9f88594482
commit
08852c522f
41
packages/components/progress/src/circular-progress.tsx
Normal file
41
packages/components/progress/src/circular-progress.tsx
Normal file
@ -0,0 +1,41 @@
|
||||
import {forwardRef} from "@nextui-org/system";
|
||||
|
||||
import {UseCircularProgressProps, useCircularProgress} from "./use-circular-progress";
|
||||
|
||||
export interface CircularProgressProps extends Omit<UseCircularProgressProps, "ref"> {}
|
||||
|
||||
const CircularProgress = forwardRef<CircularProgressProps, "div">((props, ref) => {
|
||||
const {
|
||||
Component,
|
||||
slots,
|
||||
styles,
|
||||
label,
|
||||
showValueLabel,
|
||||
getProgressBarProps,
|
||||
getLabelProps,
|
||||
getSvgProps,
|
||||
getCircleProps,
|
||||
} = useCircularProgress({ref, ...props});
|
||||
|
||||
const progressBarProps = getProgressBarProps();
|
||||
|
||||
return (
|
||||
<Component {...progressBarProps}>
|
||||
<div className={slots.svgWrapper({class: styles?.svgWrapper})}>
|
||||
<svg {...getSvgProps()}>
|
||||
<circle {...getCircleProps()} />
|
||||
</svg>
|
||||
{showValueLabel && (
|
||||
<span className={slots.value({class: styles?.value})}>
|
||||
{progressBarProps["aria-valuetext"]}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{label && <span {...getLabelProps()}>{label}</span>}
|
||||
</Component>
|
||||
);
|
||||
});
|
||||
|
||||
CircularProgress.displayName = "NextUI.CircularProgress";
|
||||
|
||||
export default CircularProgress;
|
||||
@ -1,10 +1,12 @@
|
||||
import Progress from "./progress";
|
||||
import CircularProgress from "./circular-progress";
|
||||
|
||||
// export types
|
||||
export type {ProgressProps} from "./progress";
|
||||
export type {CircularProgressProps} from "./circular-progress";
|
||||
|
||||
// export hooks
|
||||
export {useProgress} from "./use-progress";
|
||||
|
||||
// export component
|
||||
export {Progress};
|
||||
export {Progress, CircularProgress};
|
||||
|
||||
180
packages/components/progress/src/use-circular-progress.ts
Normal file
180
packages/components/progress/src/use-circular-progress.ts
Normal file
@ -0,0 +1,180 @@
|
||||
import type {
|
||||
CircularProgressVariantProps,
|
||||
SlotsToClasses,
|
||||
CircularProgressSlots,
|
||||
} from "@nextui-org/theme";
|
||||
import type {PropGetter} from "@nextui-org/system";
|
||||
import type {AriaProgressBarProps} from "@react-types/progress";
|
||||
|
||||
import {HTMLNextUIProps, mapPropsVariants} from "@nextui-org/system";
|
||||
import {circularProgress} from "@nextui-org/theme";
|
||||
import {useDOMRef} from "@nextui-org/dom-utils";
|
||||
import {clsx, dataAttr, ReactRef} from "@nextui-org/shared-utils";
|
||||
import {mergeProps} from "@react-aria/utils";
|
||||
import {useMemo, useCallback} from "react";
|
||||
import {useIsMounted} from "@nextui-org/use-is-mounted";
|
||||
|
||||
import {useProgressBar as useAriaProgress} from "./use-aria-progress";
|
||||
|
||||
export interface Props extends HTMLNextUIProps<"div"> {
|
||||
/**
|
||||
* Ref to the DOM node.
|
||||
*/
|
||||
ref?: ReactRef<HTMLElement | null>;
|
||||
/**
|
||||
* 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
|
||||
* <CircularProgress styles={{
|
||||
* base:"base-classes",
|
||||
* labelWrapper: "labelWrapper-classes",
|
||||
* label: "label-classes",
|
||||
* value: "value-classes",
|
||||
* svg: "svg-classes",
|
||||
* circle: "circle-classes",
|
||||
* }} />
|
||||
* ```
|
||||
*/
|
||||
styles?: SlotsToClasses<CircularProgressSlots>;
|
||||
}
|
||||
|
||||
export type UseCircularProgressProps = Props & AriaProgressBarProps & CircularProgressVariantProps;
|
||||
|
||||
export function useCircularProgress(originalProps: UseCircularProgressProps) {
|
||||
const [props, variantProps] = mapPropsVariants(originalProps, circularProgress.variantKeys);
|
||||
|
||||
const {
|
||||
ref,
|
||||
as,
|
||||
id,
|
||||
className,
|
||||
styles,
|
||||
label,
|
||||
valueLabel,
|
||||
value = 0,
|
||||
minValue = 0,
|
||||
maxValue = 100,
|
||||
showValueLabel = false,
|
||||
formatOptions = {
|
||||
style: "percent",
|
||||
},
|
||||
...otherProps
|
||||
} = props;
|
||||
|
||||
const Component = as || "div";
|
||||
|
||||
const domRef = useDOMRef(ref);
|
||||
|
||||
const baseStyles = clsx(styles?.base, className);
|
||||
const [, isMounted] = useIsMounted({
|
||||
rerender: true,
|
||||
delay: 100,
|
||||
});
|
||||
|
||||
const isIndeterminate = originalProps.isIndeterminate;
|
||||
|
||||
const {progressBarProps, labelProps} = useAriaProgress({
|
||||
id,
|
||||
label,
|
||||
value,
|
||||
minValue,
|
||||
maxValue,
|
||||
valueLabel,
|
||||
formatOptions,
|
||||
isIndeterminate,
|
||||
"aria-labelledby": originalProps["aria-labelledby"],
|
||||
"aria-label": originalProps["aria-label"],
|
||||
});
|
||||
|
||||
const slots = useMemo(
|
||||
() =>
|
||||
circularProgress({
|
||||
...variantProps,
|
||||
}),
|
||||
[...Object.values(variantProps)],
|
||||
);
|
||||
|
||||
const selfMounted = originalProps.disableAnimation ? true : isMounted;
|
||||
|
||||
const center = 16;
|
||||
const strokeWidth = originalProps.size === "xs" ? 2 : 3;
|
||||
const radius = 16 - strokeWidth;
|
||||
const circumference = 2 * radius * Math.PI;
|
||||
|
||||
const percentage = !selfMounted
|
||||
? 0
|
||||
: isIndeterminate
|
||||
? 0.25
|
||||
: (value - minValue) / (maxValue - minValue);
|
||||
const offset = circumference - percentage * circumference;
|
||||
|
||||
const getProgressBarProps = useCallback<PropGetter>(
|
||||
(props = {}) => ({
|
||||
ref: domRef,
|
||||
"data-indeterminate": dataAttr(isIndeterminate),
|
||||
"data-disabled": dataAttr(originalProps.isDisabled),
|
||||
className: slots.base({class: baseStyles}),
|
||||
...mergeProps(progressBarProps, otherProps, props),
|
||||
}),
|
||||
[
|
||||
domRef,
|
||||
slots,
|
||||
isIndeterminate,
|
||||
originalProps.isDisabled,
|
||||
baseStyles,
|
||||
progressBarProps,
|
||||
otherProps,
|
||||
],
|
||||
);
|
||||
|
||||
const getLabelProps = useCallback<PropGetter>(
|
||||
(props = {}) => ({
|
||||
className: slots.label({class: styles?.label}),
|
||||
...mergeProps(labelProps, props),
|
||||
}),
|
||||
[slots, styles, labelProps],
|
||||
);
|
||||
|
||||
const getSvgProps = useCallback<PropGetter>(
|
||||
(props = {}) => ({
|
||||
viewBox: "0 0 32 32",
|
||||
fill: "none",
|
||||
strokeWidth,
|
||||
className: slots.svg({class: styles?.svg}),
|
||||
...props,
|
||||
}),
|
||||
[strokeWidth, slots, styles],
|
||||
);
|
||||
|
||||
const getCircleProps = useCallback<PropGetter>(
|
||||
(props = {}) => ({
|
||||
cx: center,
|
||||
cy: center,
|
||||
r: radius,
|
||||
role: "presentation",
|
||||
strokeDasharray: `${circumference} ${circumference}`,
|
||||
strokeDashoffset: offset,
|
||||
transform: "rotate(-90 16 16)",
|
||||
className: slots.circle({class: styles?.circle}),
|
||||
...props,
|
||||
}),
|
||||
[slots, styles, offset, circumference, radius],
|
||||
);
|
||||
|
||||
return {
|
||||
Component,
|
||||
domRef,
|
||||
slots,
|
||||
styles,
|
||||
label,
|
||||
showValueLabel,
|
||||
getProgressBarProps,
|
||||
getLabelProps,
|
||||
getSvgProps,
|
||||
getCircleProps,
|
||||
};
|
||||
}
|
||||
|
||||
export type UseCircularProgressReturn = ReturnType<typeof useCircularProgress>;
|
||||
@ -0,0 +1,88 @@
|
||||
import React from "react";
|
||||
import {ComponentStory, ComponentMeta} from "@storybook/react";
|
||||
import {progress} from "@nextui-org/theme";
|
||||
|
||||
import {CircularProgress, CircularProgressProps} from "../src";
|
||||
|
||||
export default {
|
||||
title: "Components/CircularProgress",
|
||||
component: CircularProgress,
|
||||
argTypes: {
|
||||
color: {
|
||||
control: {
|
||||
type: "select",
|
||||
options: ["neutral", "primary", "secondary", "success", "warning", "danger"],
|
||||
},
|
||||
},
|
||||
size: {
|
||||
control: {
|
||||
type: "select",
|
||||
options: ["xs", "sm", "md", "lg", "xl"],
|
||||
},
|
||||
},
|
||||
isDisabled: {
|
||||
control: {
|
||||
type: "boolean",
|
||||
},
|
||||
},
|
||||
},
|
||||
} as ComponentMeta<typeof CircularProgress>;
|
||||
|
||||
const defaultProps = {
|
||||
...progress.defaultVariants,
|
||||
value: 55,
|
||||
};
|
||||
|
||||
const Template: ComponentStory<typeof CircularProgress> = (args: CircularProgressProps) => (
|
||||
<CircularProgress {...args} />
|
||||
);
|
||||
|
||||
const IntervalTemplate: ComponentStory<typeof CircularProgress> = (args: CircularProgressProps) => {
|
||||
const [value, setValue] = React.useState(0);
|
||||
|
||||
React.useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setValue((v) => (v >= 100 ? 0 : v + 10));
|
||||
}, 800);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
return <CircularProgress {...args} value={value} />;
|
||||
};
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
...defaultProps,
|
||||
"aria-label": "Loading...",
|
||||
};
|
||||
|
||||
export const WithLabel = Template.bind({});
|
||||
WithLabel.args = {
|
||||
...defaultProps,
|
||||
label: "Loading...",
|
||||
};
|
||||
|
||||
export const WithValueLabel = IntervalTemplate.bind({});
|
||||
WithValueLabel.args = {
|
||||
...defaultProps,
|
||||
size: "lg",
|
||||
color: "secondary",
|
||||
showValueLabel: true,
|
||||
};
|
||||
|
||||
export const WithValueFormatting = Template.bind({});
|
||||
WithValueFormatting.args = {
|
||||
...defaultProps,
|
||||
label: "Loading...",
|
||||
size: "xl",
|
||||
color: "warning",
|
||||
showValueLabel: true,
|
||||
formatOptions: {style: "unit", unit: "kilometer"},
|
||||
};
|
||||
|
||||
export const Indeterminate = Template.bind({});
|
||||
Indeterminate.args = {
|
||||
...defaultProps,
|
||||
isIndeterminate: true,
|
||||
};
|
||||
@ -45,6 +45,20 @@ const Template: ComponentStory<typeof Progress> = (args: ProgressProps) => (
|
||||
</div>
|
||||
);
|
||||
|
||||
const IntervalTemplate: ComponentStory<typeof Progress> = (args: ProgressProps) => {
|
||||
const [value, setValue] = React.useState(0);
|
||||
|
||||
React.useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setValue((v) => (v >= 100 ? 0 : v + 10));
|
||||
}, 800);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
return <Progress {...args} value={value} />;
|
||||
};
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
...defaultProps,
|
||||
@ -57,10 +71,11 @@ WithLabel.args = {
|
||||
label: "Loading...",
|
||||
};
|
||||
|
||||
export const WithValueLabel = Template.bind({});
|
||||
export const WithValueLabel = IntervalTemplate.bind({});
|
||||
WithValueLabel.args = {
|
||||
...defaultProps,
|
||||
label: "Loading...",
|
||||
label: "Downloading...",
|
||||
color: "success",
|
||||
showValueLabel: true,
|
||||
};
|
||||
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import {tv, type VariantProps} from "tailwind-variants";
|
||||
import type {VariantProps} from "tailwind-variants";
|
||||
|
||||
import {tv} from "tailwind-variants";
|
||||
|
||||
import {ringClasses} from "../utils";
|
||||
/**
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import {tv, VariantProps} from "tailwind-variants";
|
||||
import type {VariantProps} from "tailwind-variants";
|
||||
|
||||
import {tv} from "tailwind-variants";
|
||||
|
||||
/**
|
||||
* Accordion wrapper **Tailwind Variants** component
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import {tv, VariantProps} from "tailwind-variants";
|
||||
import type {VariantProps} from "tailwind-variants";
|
||||
|
||||
import {tv} from "tailwind-variants";
|
||||
|
||||
/**
|
||||
* AvatarGroup wrapper **Tailwind Variants** component
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import {tv, type VariantProps} from "tailwind-variants";
|
||||
import type {VariantProps} from "tailwind-variants";
|
||||
|
||||
import {tv} from "tailwind-variants";
|
||||
|
||||
import {translateCenterClasses, ringClasses, colorVariants} from "../utils";
|
||||
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import {tv, type VariantProps} from "tailwind-variants";
|
||||
import type {VariantProps} from "tailwind-variants";
|
||||
|
||||
import {tv} from "tailwind-variants";
|
||||
|
||||
import {colorVariants} from "../utils";
|
||||
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import {tv, VariantProps} from "tailwind-variants";
|
||||
import type {VariantProps} from "tailwind-variants";
|
||||
|
||||
import {tv} from "tailwind-variants";
|
||||
|
||||
/**
|
||||
* ButtonGroup wrapper **Tailwind Variants** component
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import {tv, type VariantProps} from "tailwind-variants";
|
||||
import type {VariantProps} from "tailwind-variants";
|
||||
|
||||
import {tv} from "tailwind-variants";
|
||||
|
||||
import {ringClasses, colorVariants} from "../utils";
|
||||
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import {tv, type VariantProps} from "tailwind-variants";
|
||||
import type {VariantProps} from "tailwind-variants";
|
||||
|
||||
import {tv} from "tailwind-variants";
|
||||
|
||||
import {ringClasses} from "../utils";
|
||||
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import {tv, type VariantProps} from "tailwind-variants";
|
||||
import type {VariantProps} from "tailwind-variants";
|
||||
|
||||
import {tv} from "tailwind-variants";
|
||||
|
||||
import {ringClasses} from "../utils";
|
||||
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import {tv, type VariantProps} from "tailwind-variants";
|
||||
import type {VariantProps} from "tailwind-variants";
|
||||
|
||||
import {tv} from "tailwind-variants";
|
||||
|
||||
import {ringClasses, colorVariants} from "../utils";
|
||||
|
||||
|
||||
119
packages/core/theme/src/components/circular-progress.ts
Normal file
119
packages/core/theme/src/components/circular-progress.ts
Normal file
@ -0,0 +1,119 @@
|
||||
import type {VariantProps} from "tailwind-variants";
|
||||
|
||||
import {tv} from "tailwind-variants";
|
||||
|
||||
/**
|
||||
* CircularProgress **Tailwind Variants** component
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* const {base, svgWrapper, svg, circle, value, label} = circularProgress({...})
|
||||
*
|
||||
* <div className={base()} aria-label="progress" role="progressbar" aria-valuenow={value} aria-valuemin={min} aria-valuemax={max}>
|
||||
* <div className={svgWrapper()}>
|
||||
* <svg className={svg()}>
|
||||
* <circle className={circle()} style={{width: `${value}%`}} />
|
||||
* </svg>
|
||||
* <span className={value()}>{value}</span>
|
||||
* </div>
|
||||
* <span className={label()}>{label}</span>
|
||||
* </div>
|
||||
* ```
|
||||
*/
|
||||
const circularProgress = tv({
|
||||
slots: {
|
||||
base: "flex flex-col justify-center gap-1 max-w-fit items-center",
|
||||
label: "",
|
||||
svgWrapper: "relative block",
|
||||
svg: "z-0 relative overflow-hidden",
|
||||
circle: "h-full stroke-current",
|
||||
value: "absolute font-semibold top-0 left-0 w-full h-full flex items-center justify-center",
|
||||
},
|
||||
variants: {
|
||||
color: {
|
||||
neutral: {
|
||||
svg: "text-neutral-400",
|
||||
},
|
||||
primary: {
|
||||
svg: "text-primary",
|
||||
},
|
||||
secondary: {
|
||||
svg: "text-secondary",
|
||||
},
|
||||
success: {
|
||||
svg: "text-success",
|
||||
},
|
||||
warning: {
|
||||
svg: "text-warning",
|
||||
},
|
||||
danger: {
|
||||
svg: "text-danger",
|
||||
},
|
||||
},
|
||||
size: {
|
||||
xs: {
|
||||
svg: "w-6 h-6",
|
||||
label: "text-xs",
|
||||
value: "text-[0.4rem]",
|
||||
},
|
||||
sm: {
|
||||
svg: "w-8 h-8",
|
||||
label: "text-sm",
|
||||
value: "text-[0.5rem]",
|
||||
},
|
||||
md: {
|
||||
svg: "w-10 h-10",
|
||||
label: "text-sm",
|
||||
value: "text-[0.6rem]",
|
||||
},
|
||||
lg: {
|
||||
svg: "w-12 h-12",
|
||||
label: "text-base",
|
||||
value: "text-xs",
|
||||
},
|
||||
xl: {
|
||||
svg: "w-14 h-14",
|
||||
label: "text-lg",
|
||||
value: "text-xs",
|
||||
},
|
||||
},
|
||||
isIndeterminate: {
|
||||
true: {
|
||||
svg: "animate-spinner-ease-spin",
|
||||
},
|
||||
},
|
||||
isDisabled: {
|
||||
true: {
|
||||
base: "opacity-50 cursor-not-allowed",
|
||||
},
|
||||
},
|
||||
disableAnimation: {
|
||||
true: {},
|
||||
false: {
|
||||
circle: "transition-all !duration-500",
|
||||
},
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
color: "primary",
|
||||
size: "md",
|
||||
isIndeterminate: false,
|
||||
isDisabled: false,
|
||||
disableAnimation: false,
|
||||
},
|
||||
compoundVariants: [
|
||||
// disableAnimation && !isIndeterminate
|
||||
{
|
||||
disableAnimation: true,
|
||||
isIndeterminate: false,
|
||||
class: {
|
||||
svg: "!transition-none motion-reduce:transition-none",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
export type CircularProgressVariantProps = VariantProps<typeof circularProgress>;
|
||||
export type CircularProgressSlots = keyof ReturnType<typeof circularProgress>;
|
||||
|
||||
export {circularProgress};
|
||||
@ -1,4 +1,6 @@
|
||||
import {tv, type VariantProps} from "tailwind-variants";
|
||||
import type {VariantProps} from "tailwind-variants";
|
||||
|
||||
import {tv} from "tailwind-variants";
|
||||
|
||||
import {colorVariants} from "../utils";
|
||||
|
||||
|
||||
@ -21,3 +21,4 @@ export * from "./toggle";
|
||||
export * from "./accordion-item";
|
||||
export * from "./accordion";
|
||||
export * from "./progress";
|
||||
export * from "./circular-progress";
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import {tv, type VariantProps} from "tailwind-variants";
|
||||
import type {VariantProps} from "tailwind-variants";
|
||||
|
||||
import {tv} from "tailwind-variants";
|
||||
|
||||
import {focusVisibleClasses} from "../utils";
|
||||
|
||||
@ -31,7 +33,8 @@ const link = tv({
|
||||
false: "no-underline",
|
||||
},
|
||||
isBlock: {
|
||||
true: "px-2 py-1 hover:after:opacity-100 after:content-[' '] after:inset-0 after:opacity-0 after:w-full after:h-full after:rounded-xl after:transition-background after:absolute",
|
||||
true:
|
||||
"px-2 py-1 hover:after:opacity-100 after:content-[' '] after:inset-0 after:opacity-0 after:w-full after:h-full after:rounded-xl after:transition-background after:absolute",
|
||||
false: "hover:opacity-80 transition-opacity",
|
||||
},
|
||||
isDisabled: {
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import {tv, type VariantProps} from "tailwind-variants";
|
||||
import type {VariantProps} from "tailwind-variants";
|
||||
|
||||
import {tv} from "tailwind-variants";
|
||||
|
||||
import {colorVariants, ringClasses} from "../utils";
|
||||
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import {tv, type VariantProps} from "tailwind-variants";
|
||||
import type {VariantProps} from "tailwind-variants";
|
||||
|
||||
import {tv} from "tailwind-variants";
|
||||
|
||||
/**
|
||||
* Progress **Tailwind Variants** component
|
||||
@ -139,8 +141,8 @@ const progress = tv(
|
||||
],
|
||||
},
|
||||
{
|
||||
twMerge: false
|
||||
}
|
||||
twMerge: false,
|
||||
},
|
||||
);
|
||||
|
||||
export type ProgressVariantProps = VariantProps<typeof progress>;
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import {tv, type VariantProps} from "tailwind-variants";
|
||||
import type {VariantProps} from "tailwind-variants";
|
||||
|
||||
import {tv} from "tailwind-variants";
|
||||
|
||||
import {ringClasses} from "../utils";
|
||||
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import {tv, type VariantProps} from "tailwind-variants";
|
||||
import type {VariantProps} from "tailwind-variants";
|
||||
|
||||
import {tv} from "tailwind-variants";
|
||||
|
||||
import {ringClasses, colorVariants} from "../utils";
|
||||
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import {tv, type VariantProps} from "tailwind-variants";
|
||||
import type {VariantProps} from "tailwind-variants";
|
||||
|
||||
import {tv} from "tailwind-variants";
|
||||
|
||||
/**
|
||||
* Spinner wrapper **Tailwind Variants** component
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import {tv, type VariantProps} from "tailwind-variants";
|
||||
import type {VariantProps} from "tailwind-variants";
|
||||
|
||||
import {tv} from "tailwind-variants";
|
||||
|
||||
import {ringClasses} from "../utils";
|
||||
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import {tv, type VariantProps} from "tailwind-variants";
|
||||
import type {VariantProps} from "tailwind-variants";
|
||||
|
||||
import {tv} from "tailwind-variants";
|
||||
|
||||
import {colorVariants} from "../utils";
|
||||
/**
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import {tv, VariantProps} from "tailwind-variants";
|
||||
import type {VariantProps} from "tailwind-variants";
|
||||
|
||||
import {tv} from "tailwind-variants";
|
||||
|
||||
import {ringClasses} from "../utils";
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user