mirror of
https://github.com/nextui-org/nextui.git
synced 2025-12-08 19:26:11 +00:00
feat(link): focus visible added, theme made more robust
This commit is contained in:
parent
95084d6271
commit
7081b41cbb
@ -1,41 +0,0 @@
|
|||||||
import {styled} from "@nextui-org/system";
|
|
||||||
import {cssFocusVisible} from "@nextui-org/shared-css";
|
|
||||||
|
|
||||||
export const StyledLink = styled(
|
|
||||||
"a",
|
|
||||||
{
|
|
||||||
display: "inline-flex",
|
|
||||||
alignItems: "center",
|
|
||||||
lineHeight: "inherit",
|
|
||||||
textDecoration: "none",
|
|
||||||
width: "$fit",
|
|
||||||
backgroundColor: "transparent",
|
|
||||||
backgroundImage: "inherit",
|
|
||||||
backgroundClip: "inherit",
|
|
||||||
WebkitTextFillColor: "inherit",
|
|
||||||
color: "$$linkColor",
|
|
||||||
outline: "none",
|
|
||||||
maxW: "$max",
|
|
||||||
"&:hover": {
|
|
||||||
opacity: 0.8,
|
|
||||||
},
|
|
||||||
"@motion": {
|
|
||||||
transition: "none",
|
|
||||||
},
|
|
||||||
variants: {
|
|
||||||
underline: {
|
|
||||||
true: {
|
|
||||||
"&:hover, &:active, &:focus": {
|
|
||||||
textDecoration: "underline",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
animated: {
|
|
||||||
true: {
|
|
||||||
transition: "opacity 0.2s ease 0s, background 0.2s ease 0s",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
cssFocusVisible,
|
|
||||||
);
|
|
||||||
@ -1,7 +1,8 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import {Grid} from "@nextui-org/grid";
|
import {Grid} from "@nextui-org/grid";
|
||||||
|
import {cva, linkVariants, type VariantProps, ExtendVariantProps} from "@nextui-org/theme";
|
||||||
|
|
||||||
import {Link} from "../src";
|
import {Link, LinkProps} from "../src";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: "Navigation/Link",
|
title: "Navigation/Link",
|
||||||
@ -12,6 +13,34 @@ const text = `"First solve the problem. Then, write the code." - Jon Johnson.`;
|
|||||||
|
|
||||||
export const Default = () => <Link href="#">{text}</Link>;
|
export const Default = () => <Link href="#">{text}</Link>;
|
||||||
|
|
||||||
|
const customLink = cva(null, {
|
||||||
|
variants: {
|
||||||
|
color: {
|
||||||
|
...linkVariants.color,
|
||||||
|
teal: "text-teal-600",
|
||||||
|
},
|
||||||
|
link: {
|
||||||
|
true: "before:content-['👉'] before:mr-1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
type MyLinkProps = ExtendVariantProps<LinkProps, VariantProps<typeof customLink>>;
|
||||||
|
|
||||||
|
const MyLink = (props: MyLinkProps) => {
|
||||||
|
const {link, color, ...otherProps} = props;
|
||||||
|
|
||||||
|
return <Link {...otherProps} isExternal className={customLink({color, link})} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CustomVariant = () => {
|
||||||
|
return (
|
||||||
|
<MyLink link color="teal" href="#">
|
||||||
|
Visit out new Store
|
||||||
|
</MyLink>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const Sizes = () => (
|
export const Sizes = () => (
|
||||||
<Grid.Container gap={1}>
|
<Grid.Container gap={1}>
|
||||||
<Grid xs={12}>
|
<Grid xs={12}>
|
||||||
@ -40,7 +69,7 @@ export const Sizes = () => (
|
|||||||
</Link>
|
</Link>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid xs={12}>
|
<Grid xs={12}>
|
||||||
<Link className="text-3xl text-pink-500" href="#">
|
<Link className="text-2xl text-pink-500" href="#">
|
||||||
{text}
|
{text}
|
||||||
</Link>
|
</Link>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@ -2,6 +2,8 @@ const plugin = require("tailwindcss/plugin");
|
|||||||
|
|
||||||
const colors = require("./colors.js");
|
const colors = require("./colors.js");
|
||||||
|
|
||||||
|
const DEFAULT_TRANSITION_DURATION = "200ms";
|
||||||
|
|
||||||
module.exports = plugin(
|
module.exports = plugin(
|
||||||
function ({addUtilities}) {
|
function ({addUtilities}) {
|
||||||
addUtilities({
|
addUtilities({
|
||||||
@ -23,7 +25,7 @@ module.exports = plugin(
|
|||||||
".transition-background": {
|
".transition-background": {
|
||||||
"transition-property": "background",
|
"transition-property": "background",
|
||||||
"transition-timing-function": "ease",
|
"transition-timing-function": "ease",
|
||||||
"transition-duration": "250ms",
|
"transition-duration": DEFAULT_TRANSITION_DURATION,
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Tailwind utilities
|
* Tailwind utilities
|
||||||
@ -31,36 +33,36 @@ module.exports = plugin(
|
|||||||
".transition-all": {
|
".transition-all": {
|
||||||
"transition-property": "all",
|
"transition-property": "all",
|
||||||
"transition-timing-function": "ease",
|
"transition-timing-function": "ease",
|
||||||
"transition-duration": "250ms",
|
"transition-duration": DEFAULT_TRANSITION_DURATION,
|
||||||
},
|
},
|
||||||
".transition": {
|
".transition": {
|
||||||
"transition-property":
|
"transition-property":
|
||||||
"color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter",
|
"color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter",
|
||||||
"transition-timing-function": "ease",
|
"transition-timing-function": "ease",
|
||||||
"transition-duration": "250ms",
|
"transition-duration": DEFAULT_TRANSITION_DURATION,
|
||||||
},
|
},
|
||||||
".transition-colors": {
|
".transition-colors": {
|
||||||
"transition-property":
|
"transition-property":
|
||||||
"color, background-color, border-color, text-decoration-color, fill, stroke",
|
"color, background-color, border-color, text-decoration-color, fill, stroke",
|
||||||
"transition-timing-function": "ease",
|
"transition-timing-function": "ease",
|
||||||
"transition-duration": "250ms",
|
"transition-duration": DEFAULT_TRANSITION_DURATION,
|
||||||
},
|
},
|
||||||
".transition-opacity": {
|
".transition-opacity": {
|
||||||
"transition-property": "opacity",
|
"transition-property": "opacity",
|
||||||
"transition-timing-function": "ease",
|
"transition-timing-function": "ease",
|
||||||
"transition-duration": "250ms",
|
"transition-duration": DEFAULT_TRANSITION_DURATION,
|
||||||
},
|
},
|
||||||
|
|
||||||
".transition-shadow": {
|
".transition-shadow": {
|
||||||
"transition-property": "box-shadow",
|
"transition-property": "box-shadow",
|
||||||
"transition-timing-function": "ease",
|
"transition-timing-function": "ease",
|
||||||
"transition-duration": "250ms",
|
"transition-duration": DEFAULT_TRANSITION_DURATION,
|
||||||
},
|
},
|
||||||
|
|
||||||
".transition-transform": {
|
".transition-transform": {
|
||||||
"transition-property": "transform",
|
"transition-property": "transform",
|
||||||
"transition-timing-function": "ease",
|
"transition-timing-function": "ease",
|
||||||
"transition-duration": "250ms",
|
"transition-duration": DEFAULT_TRANSITION_DURATION,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -72,6 +74,16 @@ module.exports = plugin(
|
|||||||
transparent: "transparent",
|
transparent: "transparent",
|
||||||
white: "#ffffff",
|
white: "#ffffff",
|
||||||
black: "#000000",
|
black: "#000000",
|
||||||
|
background: {
|
||||||
|
light: "#ffffff",
|
||||||
|
DEFAULT: "#ffffff",
|
||||||
|
dark: "#000000",
|
||||||
|
},
|
||||||
|
foreground: {
|
||||||
|
light: "#11181C",
|
||||||
|
DEFAULT: "#11181C",
|
||||||
|
dark: "#ECEDEE",
|
||||||
|
},
|
||||||
neutral: {
|
neutral: {
|
||||||
light: "#889096",
|
light: "#889096",
|
||||||
DEFAULT: "#889096",
|
DEFAULT: "#889096",
|
||||||
@ -102,11 +114,6 @@ module.exports = plugin(
|
|||||||
DEFAULT: colors.red[500],
|
DEFAULT: colors.red[500],
|
||||||
dark: colors.red[500],
|
dark: colors.red[500],
|
||||||
},
|
},
|
||||||
text: {
|
|
||||||
light: "#11181C",
|
|
||||||
DEFAULT: "#11181C",
|
|
||||||
dark: "#ECEDEE",
|
|
||||||
},
|
|
||||||
red: {
|
red: {
|
||||||
...colors.red,
|
...colors.red,
|
||||||
DEFAULT: colors.red[500],
|
DEFAULT: colors.red[500],
|
||||||
|
|||||||
@ -1,22 +1,20 @@
|
|||||||
import {styled, type VariantProps} from "../../utils";
|
import {cva, withFocusVisible, type VariantProps} from "../../utils";
|
||||||
|
|
||||||
export const link = styled(
|
const linkBase = withFocusVisible([
|
||||||
[
|
|
||||||
"inline-flex",
|
"inline-flex",
|
||||||
"relative",
|
"relative",
|
||||||
"items-center",
|
"items-center",
|
||||||
"leading-inherit",
|
"leading-inherit",
|
||||||
"text-current",
|
"text-current",
|
||||||
"w-fit",
|
"w-fit",
|
||||||
"outline-0",
|
"rounded-lg",
|
||||||
"bg-transparent",
|
"bg-transparent",
|
||||||
"bg-img-inherit",
|
"bg-img-inherit",
|
||||||
"bg-clip-inherit",
|
"bg-clip-inherit",
|
||||||
"text-fill-inherit",
|
"text-fill-inherit",
|
||||||
"transition-opacity",
|
]);
|
||||||
],
|
|
||||||
{
|
const linkVariants = {
|
||||||
variants: {
|
|
||||||
size: {
|
size: {
|
||||||
xs: "text-xs",
|
xs: "text-xs",
|
||||||
sm: "text-sm",
|
sm: "text-sm",
|
||||||
@ -40,10 +38,11 @@ export const link = styled(
|
|||||||
false: "hover:opacity-80",
|
false: "hover:opacity-80",
|
||||||
},
|
},
|
||||||
disableAnimation: {
|
disableAnimation: {
|
||||||
true: "after:transition-none",
|
true: "after:transition-none transition-none",
|
||||||
},
|
},
|
||||||
},
|
};
|
||||||
compoundVariants: [
|
|
||||||
|
const linkCompoundVariants = [
|
||||||
{
|
{
|
||||||
isBlock: true,
|
isBlock: true,
|
||||||
color: "primary",
|
color: "primary",
|
||||||
@ -69,15 +68,25 @@ export const link = styled(
|
|||||||
color: "error",
|
color: "error",
|
||||||
class: "hover:after:bg-error/25",
|
class: "hover:after:bg-error/25",
|
||||||
},
|
},
|
||||||
],
|
];
|
||||||
defaultVariants: {
|
|
||||||
|
const linkDefaultVariants = {
|
||||||
color: "primary",
|
color: "primary",
|
||||||
size: "md",
|
size: "md",
|
||||||
isBlock: false,
|
isBlock: false,
|
||||||
isUnderline: false,
|
isUnderline: false,
|
||||||
disableAnimation: false,
|
disableAnimation: false,
|
||||||
},
|
};
|
||||||
},
|
|
||||||
);
|
const linkStyles = {
|
||||||
|
variants: linkVariants,
|
||||||
|
compoundVariants: linkCompoundVariants,
|
||||||
|
defaultVariants: linkDefaultVariants,
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
const link = cva(linkBase, linkStyles);
|
||||||
|
|
||||||
|
export {linkBase, linkVariants, linkCompoundVariants, linkDefaultVariants, linkStyles, link};
|
||||||
|
|
||||||
export type StyledLinkProps = VariantProps<typeof link>;
|
export type StyledLinkProps = VariantProps<typeof link>;
|
||||||
|
|||||||
@ -1,5 +1,2 @@
|
|||||||
import {cva} from "class-variance-authority";
|
export {cva, cx} from "class-variance-authority";
|
||||||
export {cx} from "class-variance-authority";
|
|
||||||
export type {VariantProps} from "class-variance-authority";
|
export type {VariantProps} from "class-variance-authority";
|
||||||
|
|
||||||
export {cva, cva as styled};
|
|
||||||
|
|||||||
@ -1 +1,3 @@
|
|||||||
export * from "./cva";
|
export * from "./cva";
|
||||||
|
export * from "./styles";
|
||||||
|
export * from "./types";
|
||||||
|
|||||||
41
packages/core/theme/src/utils/styles.ts
Normal file
41
packages/core/theme/src/utils/styles.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* focus styles when the element is focused by keyboard.
|
||||||
|
*/
|
||||||
|
export const focusVisibleClasses = [
|
||||||
|
"focus:outline-0",
|
||||||
|
"focus-visible:ring-2",
|
||||||
|
"focus-visible:ring-primary",
|
||||||
|
"focus-visible:ring-offset-2",
|
||||||
|
"focus-visible:transition-shadow",
|
||||||
|
"focus-visible:ring-offset-background",
|
||||||
|
"dark:focus-visible:ring-offset-background-dark",
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function takes an array of classes and adds another array of classes to it.
|
||||||
|
* @param classes Array<string>
|
||||||
|
* @param newClasses Array<string>
|
||||||
|
* @returns Array<string>
|
||||||
|
*/
|
||||||
|
export const withClasses = (classes: Array<string>, newClasses: Array<string>) => {
|
||||||
|
// If there are existing classes, but no new classes, return the existing classes
|
||||||
|
if (!classes) {
|
||||||
|
if (!newClasses) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return newClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are new classes, but no existing classes, return the new classes
|
||||||
|
if (!newClasses) {
|
||||||
|
return classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are both new classes and existing classes, return a combination of the two
|
||||||
|
return classes.concat(newClasses);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const withFocusVisible = (classes: Array<string>) => {
|
||||||
|
return withClasses(classes, focusVisibleClasses);
|
||||||
|
};
|
||||||
27
packages/core/theme/src/utils/types.ts
Normal file
27
packages/core/theme/src/utils/types.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import type {VariantProps} from "class-variance-authority";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a utility type that allows you to extend the props of a component and add variant props.
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* import {cva, VariantProps, ExtendVariantProps} from "@nextui-org/theme";
|
||||||
|
*
|
||||||
|
* type ComponentProps = {
|
||||||
|
* foo: string;
|
||||||
|
* bar: string;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* const myComponent = cva(["text-blue-500", "font-bold"], {
|
||||||
|
* variants: {
|
||||||
|
* isFoo: {
|
||||||
|
* true: "text-red-500",
|
||||||
|
* false: "text-green-500"
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* })
|
||||||
|
*
|
||||||
|
* type MyVariantProps = VariantProps<typeof myComponent>;
|
||||||
|
*
|
||||||
|
* type MyComponentProps = ExtendVariantProps<ComponentProps, MyVariantProps>;
|
||||||
|
*/
|
||||||
|
export type ExtendVariantProps<P, T extends VariantProps<any>> = Omit<P, keyof T> & T;
|
||||||
Loading…
x
Reference in New Issue
Block a user