mirror of
https://github.com/nextui-org/nextui.git
synced 2025-12-08 19:26:11 +00:00
feat(snippet): migrated
This commit is contained in:
parent
27f2ae1fab
commit
3789144e63
@ -9,5 +9,8 @@ export type {AvatarGroupProps} from "./avatar-group";
|
||||
export {useAvatar} from "./use-avatar";
|
||||
export {useAvatarGroup} from "./use-avatar-group";
|
||||
|
||||
// export misc
|
||||
export {AvatarIcon} from "./avatar-icon";
|
||||
|
||||
// export component
|
||||
export {Avatar, AvatarGroup};
|
||||
|
||||
@ -54,7 +54,7 @@ export function useButtonGroup(originalProps: UseButtonGroupProps) {
|
||||
...variantProps,
|
||||
className,
|
||||
}),
|
||||
[variantProps, className],
|
||||
[...Object.values(variantProps), className],
|
||||
);
|
||||
|
||||
const context = useMemo<ContextType>(
|
||||
|
||||
@ -28,7 +28,7 @@ export function useCode(originalProps: UseCodeProps) {
|
||||
...variantProps,
|
||||
className,
|
||||
}),
|
||||
[variantProps, className],
|
||||
[...Object.values(variantProps), className],
|
||||
);
|
||||
|
||||
return {Component, as, styles, domRef, ...otherProps};
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
import Link from "./link";
|
||||
|
||||
// export types
|
||||
export type {LinkProps} from "./link";
|
||||
|
||||
// export hooks
|
||||
export {useLink} from "./use-link";
|
||||
|
||||
// export component
|
||||
// export misc
|
||||
export {LinkIcon} from "./link-icon";
|
||||
export {default as Link} from "./link";
|
||||
|
||||
// export component
|
||||
export {Link};
|
||||
|
||||
@ -58,7 +58,7 @@ export function useLink(originalProps: UseLinkProps) {
|
||||
...variantProps,
|
||||
className,
|
||||
}),
|
||||
[variantProps, className],
|
||||
[...Object.values(variantProps), className],
|
||||
);
|
||||
|
||||
return {Component, as, styles, domRef, linkProps, showAnchorIcon, ...otherProps};
|
||||
|
||||
@ -29,17 +29,16 @@ export default {
|
||||
},
|
||||
} as ComponentMeta<typeof Link>;
|
||||
|
||||
const text = `"First solve the problem. Then, write the code." - Jon Johnson.`;
|
||||
const children = `"First solve the problem. Then, write the code." - Jon Johnson.`;
|
||||
|
||||
const defaultProps = {
|
||||
...link.defaultVariants,
|
||||
isDisabled: false,
|
||||
showAnchorIcon: true,
|
||||
children,
|
||||
};
|
||||
|
||||
const Template: ComponentStory<typeof Link> = (args: LinkProps) => (
|
||||
<Link {...args} href="#">
|
||||
{text}
|
||||
</Link>
|
||||
);
|
||||
const Template: ComponentStory<typeof Link> = (args: LinkProps) => <Link {...args} href="#" />;
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
@ -79,9 +78,11 @@ export const isExternal = Template.bind({}) as any;
|
||||
isExternal.args = {
|
||||
...defaultProps,
|
||||
isExternal: true,
|
||||
isDisabled: false,
|
||||
showAnchorIcon: true,
|
||||
size: "md",
|
||||
};
|
||||
|
||||
export const CustomAchor = Template.bind({}) as any;
|
||||
CustomAchor.args = {
|
||||
...defaultProps,
|
||||
anchorIcon: <CustomLink />,
|
||||
};
|
||||
|
||||
|
||||
@ -41,7 +41,10 @@
|
||||
"@nextui-org/theme": "workspace:*",
|
||||
"@nextui-org/shared-utils": "workspace:*",
|
||||
"@nextui-org/shared-css": "workspace:*",
|
||||
"@nextui-org/dom-utils": "workspace:*"
|
||||
"@nextui-org/dom-utils": "workspace:*",
|
||||
"@nextui-org/use-clipboard": "workspace:*",
|
||||
"@nextui-org/tooltip": "workspace:*",
|
||||
"@react-aria/focus": "^3.9.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"clean-package": "2.1.1",
|
||||
|
||||
@ -1,5 +1,12 @@
|
||||
// export types
|
||||
export type {SnippetProps} from "./snippet";
|
||||
|
||||
// export hooks
|
||||
export {useSnippet} from "./use-snippet";
|
||||
|
||||
// export misc
|
||||
export {SnippetCheckIcon} from "./snippet-check-icon";
|
||||
export {SnippetCopyIcon} from "./snippet-copy-icon";
|
||||
|
||||
// export component
|
||||
export {default as Snippet} from "./snippet";
|
||||
|
||||
14
packages/components/snippet/src/snippet-check-icon.tsx
Normal file
14
packages/components/snippet/src/snippet-check-icon.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
export const SnippetCheckIcon = () => (
|
||||
<svg
|
||||
fill="none"
|
||||
height="1em"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
>
|
||||
<polyline points="20 6 9 17 4 12" />
|
||||
</svg>
|
||||
);
|
||||
8
packages/components/snippet/src/snippet-copy-icon.tsx
Normal file
8
packages/components/snippet/src/snippet-copy-icon.tsx
Normal file
@ -0,0 +1,8 @@
|
||||
export const SnippetCopyIcon = () => (
|
||||
<svg aria-hidden="true" height="1em" role="presentation" viewBox="0 0 24 24" width="1em">
|
||||
<path
|
||||
d="M20 2H10c-1.103 0-2 .897-2 2v4H4c-1.103 0-2 .897-2 2v10c0 1.103.897 2 2 2h10c1.103 0 2-.897 2-2v-4h4c1.103 0 2-.897 2-2V4c0-1.103-.897-2-2-2zM4 20V10h10l.002 10H4zm16-6h-4v-4c0-1.103-.897-2-2-2h-4V4h10v10z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
@ -1,23 +0,0 @@
|
||||
import {NextUI, forwardRef, HTMLNextUIProps} from "@nextui-org/system";
|
||||
import {useDOMRef} from "@nextui-org/dom-utils";
|
||||
import {clsx, __DEV__} from "@nextui-org/shared-utils";
|
||||
|
||||
export interface SnippetIconProps extends HTMLNextUIProps<"svg"> {}
|
||||
|
||||
const SnippetIcon = forwardRef<SnippetIconProps, "div">((props, ref) => {
|
||||
const {className, ...otherProps} = props;
|
||||
|
||||
const domRef = useDOMRef(ref);
|
||||
|
||||
return (
|
||||
<NextUI.Svg ref={domRef} className={clsx("nextui-snippet-icon", className)} {...otherProps} />
|
||||
);
|
||||
});
|
||||
|
||||
if (__DEV__) {
|
||||
SnippetIcon.displayName = "NextUI.SnippetIcon";
|
||||
}
|
||||
|
||||
SnippetIcon.toString = () => ".nextui-SnippetIcon";
|
||||
|
||||
export default SnippetIcon;
|
||||
@ -1,19 +1,103 @@
|
||||
import {forwardRef} from "@nextui-org/system";
|
||||
import {useDOMRef} from "@nextui-org/dom-utils";
|
||||
import {clsx, __DEV__} from "@nextui-org/shared-utils";
|
||||
import {Tooltip} from "@nextui-org/tooltip";
|
||||
import {ReactNode, useCallback, useMemo} from "react";
|
||||
|
||||
import {StyledSnippet} from "./snippet.styles";
|
||||
import {UseSnippetProps, useSnippet} from "./use-snippet";
|
||||
import {useSnippet, UseSnippetProps} from "./use-snippet";
|
||||
import {SnippetCopyIcon} from "./snippet-copy-icon";
|
||||
import {SnippetCheckIcon} from "./snippet-check-icon";
|
||||
|
||||
export interface SnippetProps extends UseSnippetProps {}
|
||||
export interface SnippetProps extends Omit<UseSnippetProps, "ref"> {}
|
||||
|
||||
const Snippet = forwardRef<SnippetProps, "div">((props, ref) => {
|
||||
const {className, ...otherProps} = useSnippet(props);
|
||||
const {
|
||||
Component,
|
||||
domRef,
|
||||
children,
|
||||
slots,
|
||||
styles,
|
||||
copied,
|
||||
copyIcon = <SnippetCopyIcon />,
|
||||
checkIcon = <SnippetCheckIcon />,
|
||||
symbolBefore,
|
||||
disableCopy,
|
||||
disableTooltip,
|
||||
hideSymbol,
|
||||
hideCopyButton,
|
||||
tooltipProps,
|
||||
isMultiLine,
|
||||
getSnippetProps,
|
||||
focusProps,
|
||||
onCopy,
|
||||
} = useSnippet({ref, ...props});
|
||||
|
||||
const domRef = useDOMRef(ref);
|
||||
const TooltipContent = useCallback(
|
||||
({children}: {children?: ReactNode}) => <Tooltip {...tooltipProps}>{children}</Tooltip>,
|
||||
[...Object.values(tooltipProps)],
|
||||
);
|
||||
|
||||
const contents = useMemo(() => {
|
||||
if (hideCopyButton) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const copyButton = (
|
||||
<button
|
||||
className={slots.copy({
|
||||
class: clsx(disableCopy && "opacity-50 cursor-not-allowed", styles?.copy),
|
||||
})}
|
||||
onClick={onCopy}
|
||||
{...focusProps}
|
||||
>
|
||||
{copied ? checkIcon : copyIcon}
|
||||
</button>
|
||||
);
|
||||
|
||||
if (disableTooltip) {
|
||||
return copyButton;
|
||||
}
|
||||
|
||||
return <TooltipContent>{copyButton}</TooltipContent>;
|
||||
}, [
|
||||
slots,
|
||||
styles?.copy,
|
||||
copied,
|
||||
checkIcon,
|
||||
copyIcon,
|
||||
onCopy,
|
||||
TooltipContent,
|
||||
disableCopy,
|
||||
disableTooltip,
|
||||
hideCopyButton,
|
||||
]);
|
||||
|
||||
const preContent = useMemo(() => {
|
||||
if (isMultiLine && children && Array.isArray(children)) {
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
{children.map((t, index) => (
|
||||
<pre key={`${index}-${t}`} className={slots.pre({class: styles?.pre})}>
|
||||
{!hideSymbol && <span className="select-none">{symbolBefore}</span>}
|
||||
{t}
|
||||
</pre>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<pre className={slots.pre({class: styles?.pre})}>
|
||||
{!hideSymbol && <span className="select-none">{symbolBefore}</span>}
|
||||
{children}
|
||||
</pre>
|
||||
);
|
||||
}, [children, hideSymbol, isMultiLine, symbolBefore, styles?.pre, slots]);
|
||||
|
||||
return (
|
||||
<StyledSnippet ref={domRef} className={clsx("nextui-snippet", className)} {...otherProps} />
|
||||
<Component ref={domRef} {...getSnippetProps()}>
|
||||
{preContent}
|
||||
{contents}
|
||||
</Component>
|
||||
);
|
||||
});
|
||||
|
||||
@ -21,6 +105,4 @@ if (__DEV__) {
|
||||
Snippet.displayName = "NextUI.Snippet";
|
||||
}
|
||||
|
||||
Snippet.toString = () => ".nextui-snippet";
|
||||
|
||||
export default Snippet;
|
||||
|
||||
@ -1,11 +1,199 @@
|
||||
import {HTMLNextUIProps} from "@nextui-org/system";
|
||||
import type {SnippetVariantProps, SnippetSlots, SlotsToClasses} from "@nextui-org/theme";
|
||||
|
||||
export interface UseSnippetProps extends HTMLNextUIProps<"div"> {}
|
||||
import {snippet} from "@nextui-org/theme";
|
||||
import {HTMLNextUIProps, mapPropsVariants} from "@nextui-org/system";
|
||||
import {useDOMRef} from "@nextui-org/dom-utils";
|
||||
import {clsx, ReactRef} from "@nextui-org/shared-utils";
|
||||
import {useClipboard} from "@nextui-org/use-clipboard";
|
||||
import {useFocusRing} from "@react-aria/focus";
|
||||
import {useMemo, useCallback} from "react";
|
||||
import {TooltipProps} from "@nextui-org/tooltip";
|
||||
export interface UseSnippetProps
|
||||
extends Omit<HTMLNextUIProps<"div">, "onCopy">,
|
||||
SnippetVariantProps {
|
||||
/**
|
||||
* Ref to the DOM node.
|
||||
*/
|
||||
ref?: ReactRef<HTMLDivElement | null>;
|
||||
/**
|
||||
* The content of the snippet.
|
||||
* if `string[]` is passed, it will be rendered as a multi-line snippet.
|
||||
*/
|
||||
children?: React.ReactNode | string | string[];
|
||||
/**
|
||||
* The symbol to show before the snippet.
|
||||
* @default "$"
|
||||
*/
|
||||
symbol?: string | React.ReactNode;
|
||||
/**
|
||||
* The time in milliseconds to wait before resetting the clipboard.
|
||||
* @default 2000
|
||||
*/
|
||||
timeout?: number;
|
||||
/*
|
||||
* Snippet copy icon.
|
||||
*/
|
||||
copyIcon?: React.ReactNode;
|
||||
/*
|
||||
* Snippet copy icon. This icon will be shown when the text is copied.
|
||||
*/
|
||||
checkIcon?: React.ReactNode;
|
||||
/**
|
||||
* 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
|
||||
* <Snippet styles={{
|
||||
* base:"base-classes",
|
||||
* pre: "pre-classes",
|
||||
* copy: "copy-classes",
|
||||
* }} />
|
||||
* ```
|
||||
*/
|
||||
styles?: SlotsToClasses<SnippetSlots>;
|
||||
/**
|
||||
* Whether the copy button should receive focus on render.
|
||||
* @default false
|
||||
*/
|
||||
autoFocus?: boolean;
|
||||
/**
|
||||
* Whether to hide the tooltip.
|
||||
* @default false
|
||||
*/
|
||||
disableTooltip?: boolean;
|
||||
/**
|
||||
* Whether to disable the copy functionality.
|
||||
* @default false
|
||||
*/
|
||||
disableCopy?: boolean;
|
||||
/**
|
||||
* Whether to hide the copy button.
|
||||
* @default false
|
||||
*/
|
||||
hideCopyButton?: boolean;
|
||||
/**
|
||||
* Whether to hide the symbol.
|
||||
* @default false
|
||||
*/
|
||||
hideSymbol?: boolean;
|
||||
/**
|
||||
* Tooltip props.
|
||||
*/
|
||||
tooltipProps?: TooltipProps;
|
||||
/**
|
||||
* Callback when the text is copied.
|
||||
*/
|
||||
onCopy?: (value: string | string[]) => void;
|
||||
}
|
||||
|
||||
export function useSnippet(props: UseSnippetProps) {
|
||||
const {...otherProps} = props;
|
||||
export function useSnippet(originalProps: UseSnippetProps) {
|
||||
const [props, variantProps] = mapPropsVariants(originalProps, snippet.variantKeys);
|
||||
|
||||
return {...otherProps};
|
||||
const {
|
||||
ref,
|
||||
as,
|
||||
children,
|
||||
symbol = "$",
|
||||
styles,
|
||||
timeout,
|
||||
copyIcon,
|
||||
checkIcon,
|
||||
disableCopy = false,
|
||||
disableTooltip = false,
|
||||
hideCopyButton = false,
|
||||
autoFocus = false,
|
||||
hideSymbol = false,
|
||||
onCopy: onCopyProp,
|
||||
tooltipProps = {
|
||||
offset: 12,
|
||||
content: "Copy to clipboard",
|
||||
size: originalProps?.size as TooltipProps["size"],
|
||||
variant: originalProps?.variant as TooltipProps["variant"],
|
||||
color: originalProps?.color as TooltipProps["color"],
|
||||
isDisabled: disableCopy,
|
||||
},
|
||||
className,
|
||||
...otherProps
|
||||
} = props;
|
||||
|
||||
const Component = as || "div";
|
||||
|
||||
const domRef = useDOMRef(ref);
|
||||
|
||||
const {copy, copied} = useClipboard({timeout});
|
||||
|
||||
const isMultiLine = children && Array.isArray(children);
|
||||
|
||||
const {isFocusVisible, focusProps} = useFocusRing({
|
||||
autoFocus,
|
||||
});
|
||||
|
||||
const slots = useMemo(
|
||||
() =>
|
||||
snippet({
|
||||
...variantProps,
|
||||
isFocusVisible,
|
||||
}),
|
||||
[...Object.values(variantProps), isFocusVisible],
|
||||
);
|
||||
|
||||
const symbolBefore = useMemo(() => {
|
||||
if (!symbol || typeof symbol !== "string") return symbol;
|
||||
const str = symbol.trim();
|
||||
|
||||
return str ? `${str} ` : "";
|
||||
}, [symbol]);
|
||||
|
||||
const baseStyles = clsx(styles?.base, className);
|
||||
|
||||
const getSnippetProps = useCallback(
|
||||
() => ({
|
||||
className: slots.base({
|
||||
class: baseStyles,
|
||||
}),
|
||||
...otherProps,
|
||||
}),
|
||||
[slots, baseStyles, isMultiLine, otherProps],
|
||||
);
|
||||
|
||||
const onCopy = useCallback(() => {
|
||||
if (disableCopy) {
|
||||
return;
|
||||
}
|
||||
let value = "";
|
||||
|
||||
if (typeof children === "string") {
|
||||
value = children;
|
||||
} else if (Array.isArray(children)) {
|
||||
value = children.join("\n");
|
||||
}
|
||||
copy(value);
|
||||
onCopyProp?.(value);
|
||||
}, [copy, disableCopy, onCopyProp, children]);
|
||||
|
||||
return {
|
||||
Component,
|
||||
as,
|
||||
domRef,
|
||||
children,
|
||||
slots,
|
||||
styles,
|
||||
copied,
|
||||
onCopy,
|
||||
copyIcon,
|
||||
checkIcon,
|
||||
symbolBefore,
|
||||
isMultiLine,
|
||||
isFocusVisible,
|
||||
focusProps,
|
||||
hideCopyButton,
|
||||
disableCopy,
|
||||
disableTooltip,
|
||||
hideSymbol,
|
||||
tooltipProps,
|
||||
getSnippetProps,
|
||||
};
|
||||
}
|
||||
|
||||
export type UseSnippetReturn = ReturnType<typeof useSnippet>;
|
||||
|
||||
@ -1,11 +1,87 @@
|
||||
import React from "react";
|
||||
import {Meta} from "@storybook/react";
|
||||
import {ComponentStory, ComponentMeta} from "@storybook/react";
|
||||
import {snippet} from "@nextui-org/theme";
|
||||
|
||||
import {Snippet} from "../src";
|
||||
import {Snippet, SnippetProps} from "../src";
|
||||
|
||||
export default {
|
||||
title: "Snippet",
|
||||
title: "Display/Snippet",
|
||||
component: Snippet,
|
||||
} as Meta;
|
||||
argTypes: {
|
||||
variant: {
|
||||
control: {
|
||||
type: "select",
|
||||
options: ["flat", "solid", "bordered", "shadow"],
|
||||
},
|
||||
},
|
||||
color: {
|
||||
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"],
|
||||
},
|
||||
},
|
||||
fullWidth: {
|
||||
control: {
|
||||
type: "boolean",
|
||||
},
|
||||
},
|
||||
disableCopy: {
|
||||
control: {
|
||||
type: "boolean",
|
||||
},
|
||||
},
|
||||
disableTooltip: {
|
||||
control: {
|
||||
type: "boolean",
|
||||
},
|
||||
},
|
||||
hideCopyButton: {
|
||||
control: {
|
||||
type: "boolean",
|
||||
},
|
||||
},
|
||||
hideSymbol: {
|
||||
control: {
|
||||
type: "boolean",
|
||||
},
|
||||
},
|
||||
},
|
||||
} as ComponentMeta<typeof Snippet>;
|
||||
|
||||
export const Default = () => <Snippet />;
|
||||
const defaultProps = {
|
||||
children: "npm install @nextui-org/react",
|
||||
disableCopy: false,
|
||||
disableTooltip: false,
|
||||
hideCopyButton: false,
|
||||
hideSymbol: false,
|
||||
...snippet.defaultVariants,
|
||||
};
|
||||
|
||||
const Template: ComponentStory<typeof Snippet> = (args: SnippetProps) => <Snippet {...args} />;
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
...defaultProps,
|
||||
};
|
||||
|
||||
export const MultiLine = Template.bind({});
|
||||
MultiLine.args = {
|
||||
...defaultProps,
|
||||
children: [
|
||||
"npm install @nextui-org/react",
|
||||
"yarn add @nextui-org/react",
|
||||
"pnpm add @nextui-org/react",
|
||||
],
|
||||
};
|
||||
|
||||
@ -16,7 +16,7 @@ export interface UseSpinnerProps extends HTMLNextUIProps<"div", SpinnerVariantPr
|
||||
*/
|
||||
label?: string;
|
||||
/**
|
||||
* Classname or List of classes to change the styles of the avatar.
|
||||
* 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
|
||||
@ -39,7 +39,7 @@ export function useSpinner(originalProps: UseSpinnerProps) {
|
||||
|
||||
const domRef = useDOMRef(ref);
|
||||
|
||||
const slots = useMemo(() => spinner({...variantProps}), [variantProps]);
|
||||
const slots = useMemo(() => spinner({...variantProps}), [...Object.values(variantProps)]);
|
||||
|
||||
const baseStyles = clsx(styles?.base, className);
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@ module.exports = {
|
||||
"../../spinner/stories/*.stories.@(js|jsx|ts|tsx)",
|
||||
"../../code/stories/*.stories.@(js|jsx|ts|tsx)",
|
||||
"../../tooltip/stories/*.stories.@(js|jsx|ts|tsx)",
|
||||
"../../snippet/stories/*.stories.@(js|jsx|ts|tsx)",
|
||||
],
|
||||
staticDirs: ["../public"],
|
||||
addons: [
|
||||
@ -14,7 +15,17 @@ module.exports = {
|
||||
"@storybook/addon-essentials",
|
||||
"storybook-dark-mode",
|
||||
"@storybook/addon-a11y",
|
||||
"@storybook/addon-storysource",
|
||||
{
|
||||
name: '@storybook/addon-storysource',
|
||||
options: {
|
||||
rule: {
|
||||
test: [/\.stories\.(js|jsx|ts|tsx)$/],
|
||||
},
|
||||
loaderOptions: {
|
||||
prettierConfig: { printWidth: 80, singleQuote: false },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "@storybook/addon-postcss",
|
||||
options: {
|
||||
|
||||
@ -48,7 +48,7 @@
|
||||
"@storybook/addon-interactions": "^6.5.10",
|
||||
"@storybook/addon-links": "^6.5.12",
|
||||
"@storybook/addon-postcss": "^2.0.0",
|
||||
"@storybook/addon-storysource": "^6.5.12",
|
||||
"@storybook/addon-storysource": "^6.5.16",
|
||||
"@storybook/addons": "^6.5.16",
|
||||
"@storybook/builder-webpack5": "^6.5.12",
|
||||
"@storybook/manager-webpack5": "^6.5.12",
|
||||
@ -56,9 +56,9 @@
|
||||
"@storybook/theming": "^6.5.16",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"babel-loader": "^8.2.3",
|
||||
"concurrently": "^7.6.0",
|
||||
"postcss": "^8.4.21",
|
||||
"storybook-dark-mode": "^1.1.2",
|
||||
"concurrently": "^7.6.0",
|
||||
"tailwindcss": "^3.2.4"
|
||||
},
|
||||
"tsup": {
|
||||
|
||||
@ -159,9 +159,9 @@ export function useTooltip(originalProps: UseTooltipProps) {
|
||||
|
||||
const transitionProps = useMemo<CSSTransitionProps>(
|
||||
() => ({
|
||||
clearTime: originalProps?.disableAnimation ? 0 : 100,
|
||||
enterTime: originalProps?.disableAnimation ? 0 : 250,
|
||||
leaveTime: originalProps?.disableAnimation ? 0 : 60,
|
||||
clearTime: originalProps?.disableAnimation ? 0 : 60,
|
||||
isVisible: state.isOpen,
|
||||
onEntered: onEntered,
|
||||
onExited: onExited,
|
||||
@ -175,7 +175,7 @@ export function useTooltip(originalProps: UseTooltipProps) {
|
||||
...variantProps,
|
||||
className,
|
||||
}),
|
||||
[variantProps, className],
|
||||
[...Object.values(variantProps), className],
|
||||
);
|
||||
|
||||
const getTriggerProps = useCallback(
|
||||
@ -183,7 +183,7 @@ export function useTooltip(originalProps: UseTooltipProps) {
|
||||
...mergeProps(triggerProps, props),
|
||||
ref: mergeRefs(triggerRef, _ref),
|
||||
onPointerEnter: () => state.open(),
|
||||
onPointerLeave: () => !isDismissable && state.close(),
|
||||
onPointerLeave: () => isDismissable && state.close(),
|
||||
}),
|
||||
[isDismissable, triggerRef, triggerProps],
|
||||
);
|
||||
|
||||
@ -101,6 +101,30 @@ export function rootStyled<T extends As, P = {}>(component: T) {
|
||||
return Component as NextUIComponent<T, P>;
|
||||
}
|
||||
|
||||
export const toIterator = (obj: any) => {
|
||||
return {
|
||||
...obj,
|
||||
[Symbol.iterator]: function () {
|
||||
const keys = Object.keys(this);
|
||||
let index = 0;
|
||||
|
||||
return {
|
||||
next: () => {
|
||||
if (index >= keys.length) {
|
||||
return {done: true};
|
||||
}
|
||||
const key = keys[index];
|
||||
const value = this[key];
|
||||
|
||||
index++;
|
||||
|
||||
return {value: {key, value}, done: false};
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const mapPropsVariants = <T extends Record<string, any>, K extends keyof T>(
|
||||
props: T,
|
||||
variantKeys?: K[],
|
||||
|
||||
@ -8,3 +8,4 @@ export * from "./drip";
|
||||
export * from "./spinner";
|
||||
export * from "./code";
|
||||
export * from "./tooltip";
|
||||
export * from "./snippet";
|
||||
|
||||
@ -1,22 +1,253 @@
|
||||
import {tv, type VariantProps} from "tailwind-variants";
|
||||
|
||||
import {ringClasses} from "../../utils";
|
||||
|
||||
/**
|
||||
* Snippet wrapper **Tailwind Variants** component
|
||||
*
|
||||
* const {base, svg} = snippet({...})
|
||||
* const {base, pre, copy} = snippet({...})
|
||||
*
|
||||
* @example
|
||||
* <div className={base())}>
|
||||
* <svg className={svg()}>
|
||||
* // drip svg content
|
||||
* </svg>
|
||||
* <pre className={pre()}>
|
||||
* // code snippet
|
||||
* </pre>
|
||||
* <button className={copy()}>
|
||||
* <svg>
|
||||
* // copy icon
|
||||
* </svg>
|
||||
* </button>
|
||||
* </div>
|
||||
*/
|
||||
const snippet = tv({
|
||||
slots: {
|
||||
base: "",
|
||||
svg: "",
|
||||
base: "inline-flex items-center justify-between space-x-3 rounded-md",
|
||||
pre: "bg-transparent text-inherit font-mono whitespace-pre-wrap",
|
||||
copy: "z-10 apparance-none outline-none select-none",
|
||||
},
|
||||
variants: {
|
||||
variant: {
|
||||
flat: "",
|
||||
solid: "",
|
||||
bordered: "border-2 !bg-transparent",
|
||||
shadow: "",
|
||||
},
|
||||
color: {
|
||||
neutral: {
|
||||
base: "text-neutral-800 bg-neutral-100 dark:text-neutral-100 dark:bg-neutral-800",
|
||||
},
|
||||
primary: {
|
||||
base: "bg-primary-50 dark:bg-primary-900 text-primary",
|
||||
},
|
||||
secondary: {
|
||||
base: "bg-secondary-50 dark:bg-secondary-900 text-secondary dark:text-secondary-400",
|
||||
},
|
||||
success: {
|
||||
base: "bg-success-50 dark:bg-success-900 text-success-600 dark:text-success",
|
||||
},
|
||||
warning: {
|
||||
base: "bg-warning-50 dark:bg-warning-900 text-warning-600 dark:text-warning",
|
||||
},
|
||||
danger: {
|
||||
base: "bg-danger-50 dark:bg-danger-900 text-danger",
|
||||
},
|
||||
},
|
||||
size: {
|
||||
xs: {
|
||||
base: "px-2 py-1 text-xs",
|
||||
},
|
||||
sm: {
|
||||
base: "px-2 py-1 text-sm",
|
||||
},
|
||||
md: {
|
||||
base: "px-3 py-1.5 text-base",
|
||||
},
|
||||
lg: {
|
||||
base: "px-4 py-2 text-lg",
|
||||
},
|
||||
xl: {
|
||||
base: "px-4 py-2 text-xl",
|
||||
},
|
||||
},
|
||||
radius: {
|
||||
none: {
|
||||
base: "rounded-none",
|
||||
},
|
||||
base: {
|
||||
base: "rounded-base",
|
||||
},
|
||||
sm: {
|
||||
base: "rounded-sm",
|
||||
},
|
||||
md: {
|
||||
base: "rounded-md",
|
||||
},
|
||||
lg: {
|
||||
base: "rounded-lg",
|
||||
},
|
||||
xl: {
|
||||
base: "rounded-xl",
|
||||
},
|
||||
full: {
|
||||
base: "rounded-full",
|
||||
},
|
||||
},
|
||||
fullWidth: {
|
||||
true: {
|
||||
base: "w-full",
|
||||
},
|
||||
},
|
||||
isFocusVisible: {
|
||||
true: {
|
||||
copy: [
|
||||
...ringClasses,
|
||||
"ring-1",
|
||||
"rounded-sm",
|
||||
"ring-offset-transparent",
|
||||
"dark:ring-offset-transparent",
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
color: "neutral",
|
||||
variant: "flat",
|
||||
size: "md",
|
||||
radius: "lg",
|
||||
fullWidth: false,
|
||||
isFocusVisible: false,
|
||||
},
|
||||
compoundVariants: [
|
||||
// solid & shadow / color
|
||||
{
|
||||
variant: ["solid", "shadow"],
|
||||
color: "neutral",
|
||||
class: {
|
||||
base: "bg-neutral-300 dark:bg-neutral-700 text-neutral-800 dark:text-neutral-100",
|
||||
},
|
||||
},
|
||||
{
|
||||
variant: ["solid", "shadow"],
|
||||
color: "primary",
|
||||
class: {
|
||||
base: "bg-primary text-white",
|
||||
},
|
||||
},
|
||||
{
|
||||
variant: ["solid", "shadow"],
|
||||
color: "secondary",
|
||||
class: {
|
||||
base: "bg-secondary text-white",
|
||||
},
|
||||
},
|
||||
{
|
||||
variant: ["solid", "shadow"],
|
||||
color: "success",
|
||||
class: {
|
||||
base: "bg-success text-success-800",
|
||||
},
|
||||
},
|
||||
{
|
||||
variant: ["solid", "shadow"],
|
||||
color: "warning",
|
||||
class: {
|
||||
base: "bg-warning text-warning-800",
|
||||
},
|
||||
},
|
||||
{
|
||||
variant: ["solid", "shadow"],
|
||||
color: "danger",
|
||||
class: {
|
||||
base: "bg-danger text-white",
|
||||
},
|
||||
},
|
||||
// shadow / color
|
||||
{
|
||||
variant: "shadow",
|
||||
color: "neutral",
|
||||
class: {
|
||||
base: "shadow-lg shadow-neutral/40",
|
||||
},
|
||||
},
|
||||
{
|
||||
variant: "shadow",
|
||||
color: "primary",
|
||||
class: {
|
||||
base: "shadow-lg shadow-primary/40",
|
||||
},
|
||||
},
|
||||
{
|
||||
variant: "shadow",
|
||||
color: "secondary",
|
||||
class: {
|
||||
base: "shadow-lg shadow-secondary/40",
|
||||
},
|
||||
},
|
||||
{
|
||||
variant: "shadow",
|
||||
color: "success",
|
||||
class: {
|
||||
base: "shadow-lg shadow-success/40",
|
||||
},
|
||||
},
|
||||
{
|
||||
variant: "shadow",
|
||||
color: "warning",
|
||||
class: {
|
||||
base: "shadow-lg shadow-warning/40",
|
||||
},
|
||||
},
|
||||
{
|
||||
variant: "shadow",
|
||||
color: "danger",
|
||||
class: {
|
||||
base: "shadow-lg shadow-danger/40",
|
||||
},
|
||||
},
|
||||
// bordered / color
|
||||
{
|
||||
variant: "bordered",
|
||||
color: "neutral",
|
||||
class: {
|
||||
base: "border-neutral-300 dark:border-neutral-700 text-neutral-700 dark:text-neutral-100",
|
||||
},
|
||||
},
|
||||
{
|
||||
variant: "bordered",
|
||||
color: "primary",
|
||||
class: {
|
||||
base: "border-primary text-primary",
|
||||
},
|
||||
},
|
||||
{
|
||||
variant: "bordered",
|
||||
color: "secondary",
|
||||
class: {
|
||||
base: "border-secondary text-secondary",
|
||||
},
|
||||
},
|
||||
{
|
||||
variant: "bordered",
|
||||
color: "success",
|
||||
class: {
|
||||
base: "border-success text-success",
|
||||
},
|
||||
},
|
||||
{
|
||||
variant: "bordered",
|
||||
color: "warning",
|
||||
class: {
|
||||
base: "border-warning text-warning",
|
||||
},
|
||||
},
|
||||
{
|
||||
variant: "bordered",
|
||||
color: "danger",
|
||||
class: {
|
||||
base: "border-danger text-danger",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
export type SnippetVariantProps = VariantProps<typeof snippet>;
|
||||
|
||||
@ -46,8 +46,8 @@ const spinner = tv({
|
||||
size: {
|
||||
xs: {
|
||||
base: "w-4 h-4",
|
||||
circle1: "border-2",
|
||||
circle2: "border-2",
|
||||
circle1: "border-1.5",
|
||||
circle2: "border-1.5",
|
||||
label: "translate-y-6 text-xs",
|
||||
},
|
||||
sm: {
|
||||
@ -58,14 +58,14 @@ const spinner = tv({
|
||||
},
|
||||
md: {
|
||||
base: "w-8 h-8",
|
||||
circle1: "border-[3px]",
|
||||
circle2: "border-[3px]",
|
||||
circle1: "border-3",
|
||||
circle2: "border-3",
|
||||
label: "translate-y-8 text-sm",
|
||||
},
|
||||
lg: {
|
||||
base: "w-10 h-10",
|
||||
circle1: "border-[3px]",
|
||||
circle2: "border-[3px]",
|
||||
circle1: "border-3",
|
||||
circle2: "border-3",
|
||||
label: "translate-y-10 text-base",
|
||||
},
|
||||
xl: {
|
||||
|
||||
@ -212,6 +212,12 @@ const tooltip = tv({
|
||||
color: "danger",
|
||||
class: "text-danger",
|
||||
},
|
||||
// size (xs) / bordered
|
||||
{
|
||||
size: "xs",
|
||||
variant: "bordered",
|
||||
class: "border-1.5",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
|
||||
@ -151,6 +151,11 @@ module.exports = plugin(
|
||||
DEFAULT: colors.pink[500],
|
||||
},
|
||||
},
|
||||
borderWidth: {
|
||||
1.5: "1.5px",
|
||||
3: "3px",
|
||||
5: "5px",
|
||||
},
|
||||
animation: {
|
||||
"drip-expand": "drip-expand 350ms linear",
|
||||
"spinner-ease-spin": "spinner-spin 0.8s ease infinite",
|
||||
|
||||
@ -29,7 +29,7 @@ export function use{{capitalize componentName}}(originalProps: Use{{capitalize c
|
||||
...variantProps,
|
||||
className,
|
||||
}),
|
||||
[variantProps, className],
|
||||
[...Object.values(variantProps), className],
|
||||
);
|
||||
|
||||
return {Component, styles, domRef, ...otherProps};
|
||||
|
||||
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@ -658,6 +658,9 @@ importers:
|
||||
'@nextui-org/shared-utils': workspace:*
|
||||
'@nextui-org/system': workspace:*
|
||||
'@nextui-org/theme': workspace:*
|
||||
'@nextui-org/tooltip': workspace:*
|
||||
'@nextui-org/use-clipboard': workspace:*
|
||||
'@react-aria/focus': ^3.9.0
|
||||
clean-package: 2.1.1
|
||||
react: ^17.0.2
|
||||
dependencies:
|
||||
@ -666,6 +669,9 @@ importers:
|
||||
'@nextui-org/shared-utils': link:../../utilities/shared-utils
|
||||
'@nextui-org/system': link:../../core/system
|
||||
'@nextui-org/theme': link:../../core/theme
|
||||
'@nextui-org/tooltip': link:../tooltip
|
||||
'@nextui-org/use-clipboard': link:../../hooks/use-clipboard
|
||||
'@react-aria/focus': 3.10.1_react@17.0.2
|
||||
devDependencies:
|
||||
clean-package: 2.1.1
|
||||
react: 17.0.2
|
||||
@ -701,7 +707,7 @@ importers:
|
||||
'@storybook/addon-interactions': ^6.5.10
|
||||
'@storybook/addon-links': ^6.5.12
|
||||
'@storybook/addon-postcss': ^2.0.0
|
||||
'@storybook/addon-storysource': ^6.5.12
|
||||
'@storybook/addon-storysource': ^6.5.16
|
||||
'@storybook/addons': ^6.5.16
|
||||
'@storybook/builder-webpack5': ^6.5.12
|
||||
'@storybook/manager-webpack5': ^6.5.12
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user