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 {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 {
|
||||
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>;
|
||||
|
||||
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 = () => (
|
||||
<Grid.Container gap={1}>
|
||||
<Grid xs={12}>
|
||||
@ -40,7 +69,7 @@ export const Sizes = () => (
|
||||
</Link>
|
||||
</Grid>
|
||||
<Grid xs={12}>
|
||||
<Link className="text-3xl text-pink-500" href="#">
|
||||
<Link className="text-2xl text-pink-500" href="#">
|
||||
{text}
|
||||
</Link>
|
||||
</Grid>
|
||||
|
||||
@ -2,6 +2,8 @@ const plugin = require("tailwindcss/plugin");
|
||||
|
||||
const colors = require("./colors.js");
|
||||
|
||||
const DEFAULT_TRANSITION_DURATION = "200ms";
|
||||
|
||||
module.exports = plugin(
|
||||
function ({addUtilities}) {
|
||||
addUtilities({
|
||||
@ -23,7 +25,7 @@ module.exports = plugin(
|
||||
".transition-background": {
|
||||
"transition-property": "background",
|
||||
"transition-timing-function": "ease",
|
||||
"transition-duration": "250ms",
|
||||
"transition-duration": DEFAULT_TRANSITION_DURATION,
|
||||
},
|
||||
/**
|
||||
* Tailwind utilities
|
||||
@ -31,36 +33,36 @@ module.exports = plugin(
|
||||
".transition-all": {
|
||||
"transition-property": "all",
|
||||
"transition-timing-function": "ease",
|
||||
"transition-duration": "250ms",
|
||||
"transition-duration": DEFAULT_TRANSITION_DURATION,
|
||||
},
|
||||
".transition": {
|
||||
"transition-property":
|
||||
"color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter",
|
||||
"transition-timing-function": "ease",
|
||||
"transition-duration": "250ms",
|
||||
"transition-duration": DEFAULT_TRANSITION_DURATION,
|
||||
},
|
||||
".transition-colors": {
|
||||
"transition-property":
|
||||
"color, background-color, border-color, text-decoration-color, fill, stroke",
|
||||
"transition-timing-function": "ease",
|
||||
"transition-duration": "250ms",
|
||||
"transition-duration": DEFAULT_TRANSITION_DURATION,
|
||||
},
|
||||
".transition-opacity": {
|
||||
"transition-property": "opacity",
|
||||
"transition-timing-function": "ease",
|
||||
"transition-duration": "250ms",
|
||||
"transition-duration": DEFAULT_TRANSITION_DURATION,
|
||||
},
|
||||
|
||||
".transition-shadow": {
|
||||
"transition-property": "box-shadow",
|
||||
"transition-timing-function": "ease",
|
||||
"transition-duration": "250ms",
|
||||
"transition-duration": DEFAULT_TRANSITION_DURATION,
|
||||
},
|
||||
|
||||
".transition-transform": {
|
||||
"transition-property": "transform",
|
||||
"transition-timing-function": "ease",
|
||||
"transition-duration": "250ms",
|
||||
"transition-duration": DEFAULT_TRANSITION_DURATION,
|
||||
},
|
||||
});
|
||||
},
|
||||
@ -72,6 +74,16 @@ module.exports = plugin(
|
||||
transparent: "transparent",
|
||||
white: "#ffffff",
|
||||
black: "#000000",
|
||||
background: {
|
||||
light: "#ffffff",
|
||||
DEFAULT: "#ffffff",
|
||||
dark: "#000000",
|
||||
},
|
||||
foreground: {
|
||||
light: "#11181C",
|
||||
DEFAULT: "#11181C",
|
||||
dark: "#ECEDEE",
|
||||
},
|
||||
neutral: {
|
||||
light: "#889096",
|
||||
DEFAULT: "#889096",
|
||||
@ -102,11 +114,6 @@ module.exports = plugin(
|
||||
DEFAULT: colors.red[500],
|
||||
dark: colors.red[500],
|
||||
},
|
||||
text: {
|
||||
light: "#11181C",
|
||||
DEFAULT: "#11181C",
|
||||
dark: "#ECEDEE",
|
||||
},
|
||||
red: {
|
||||
...colors.red,
|
||||
DEFAULT: colors.red[500],
|
||||
|
||||
@ -1,83 +1,92 @@
|
||||
import {styled, type VariantProps} from "../../utils";
|
||||
import {cva, withFocusVisible, type VariantProps} from "../../utils";
|
||||
|
||||
export const link = styled(
|
||||
[
|
||||
"inline-flex",
|
||||
"relative",
|
||||
"items-center",
|
||||
"leading-inherit",
|
||||
"text-current",
|
||||
"w-fit",
|
||||
"outline-0",
|
||||
"bg-transparent",
|
||||
"bg-img-inherit",
|
||||
"bg-clip-inherit",
|
||||
"text-fill-inherit",
|
||||
"transition-opacity",
|
||||
],
|
||||
{
|
||||
variants: {
|
||||
size: {
|
||||
xs: "text-xs",
|
||||
sm: "text-sm",
|
||||
md: "text-base",
|
||||
lg: "text-lg",
|
||||
xl: "text-xl",
|
||||
},
|
||||
color: {
|
||||
primary: "text-primary",
|
||||
secondary: "text-secondary dark:text-secondary-dark",
|
||||
success: "text-success",
|
||||
warning: "text-warning",
|
||||
error: "text-error",
|
||||
},
|
||||
isUnderline: {
|
||||
true: "hover:underline active:underline focus:underline",
|
||||
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",
|
||||
false: "hover:opacity-80",
|
||||
},
|
||||
disableAnimation: {
|
||||
true: "after:transition-none",
|
||||
},
|
||||
},
|
||||
compoundVariants: [
|
||||
{
|
||||
isBlock: true,
|
||||
color: "primary",
|
||||
class: "hover:after:bg-primary/25",
|
||||
},
|
||||
{
|
||||
isBlock: true,
|
||||
color: "secondary",
|
||||
class: "hover:after:bg-secondary/25 dark:hover:after:bg-secondary-dark/25",
|
||||
},
|
||||
{
|
||||
isBlock: true,
|
||||
color: "success",
|
||||
class: "hover:after:bg-success/25",
|
||||
},
|
||||
{
|
||||
isBlock: true,
|
||||
color: "warning",
|
||||
class: "hover:after:bg-warning/25",
|
||||
},
|
||||
{
|
||||
isBlock: true,
|
||||
color: "error",
|
||||
class: "hover:after:bg-error/25",
|
||||
},
|
||||
],
|
||||
defaultVariants: {
|
||||
color: "primary",
|
||||
size: "md",
|
||||
isBlock: false,
|
||||
isUnderline: false,
|
||||
disableAnimation: false,
|
||||
},
|
||||
const linkBase = withFocusVisible([
|
||||
"inline-flex",
|
||||
"relative",
|
||||
"items-center",
|
||||
"leading-inherit",
|
||||
"text-current",
|
||||
"w-fit",
|
||||
"rounded-lg",
|
||||
"bg-transparent",
|
||||
"bg-img-inherit",
|
||||
"bg-clip-inherit",
|
||||
"text-fill-inherit",
|
||||
]);
|
||||
|
||||
const linkVariants = {
|
||||
size: {
|
||||
xs: "text-xs",
|
||||
sm: "text-sm",
|
||||
md: "text-base",
|
||||
lg: "text-lg",
|
||||
xl: "text-xl",
|
||||
},
|
||||
);
|
||||
color: {
|
||||
primary: "text-primary",
|
||||
secondary: "text-secondary dark:text-secondary-dark",
|
||||
success: "text-success",
|
||||
warning: "text-warning",
|
||||
error: "text-error",
|
||||
},
|
||||
isUnderline: {
|
||||
true: "hover:underline active:underline focus:underline",
|
||||
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",
|
||||
false: "hover:opacity-80",
|
||||
},
|
||||
disableAnimation: {
|
||||
true: "after:transition-none transition-none",
|
||||
},
|
||||
};
|
||||
|
||||
const linkCompoundVariants = [
|
||||
{
|
||||
isBlock: true,
|
||||
color: "primary",
|
||||
class: "hover:after:bg-primary/25",
|
||||
},
|
||||
{
|
||||
isBlock: true,
|
||||
color: "secondary",
|
||||
class: "hover:after:bg-secondary/25 dark:hover:after:bg-secondary-dark/25",
|
||||
},
|
||||
{
|
||||
isBlock: true,
|
||||
color: "success",
|
||||
class: "hover:after:bg-success/25",
|
||||
},
|
||||
{
|
||||
isBlock: true,
|
||||
color: "warning",
|
||||
class: "hover:after:bg-warning/25",
|
||||
},
|
||||
{
|
||||
isBlock: true,
|
||||
color: "error",
|
||||
class: "hover:after:bg-error/25",
|
||||
},
|
||||
];
|
||||
|
||||
const linkDefaultVariants = {
|
||||
color: "primary",
|
||||
size: "md",
|
||||
isBlock: false,
|
||||
isUnderline: 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>;
|
||||
|
||||
@ -1,5 +1,2 @@
|
||||
import {cva} from "class-variance-authority";
|
||||
export {cx} from "class-variance-authority";
|
||||
export {cva, cx} from "class-variance-authority";
|
||||
export type {VariantProps} from "class-variance-authority";
|
||||
|
||||
export {cva, cva as styled};
|
||||
|
||||
@ -1 +1,3 @@
|
||||
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