Skip to content

Commit

Permalink
Merge pull request #19 from gregberge/various-improvements
Browse files Browse the repository at this point in the history
various improvements
  • Loading branch information
gregberge authored Dec 23, 2023
2 parents 7852449 + 593b675 commit fafca5d
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 29 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
"exports": {
".": {
"import": "./dist/index.mjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts"
}
},
"types": "./dist/index.d.ts",
"scripts": {
"test": "vitest",
"build": "rm -rf dist && rollup -c",
Expand Down
5 changes: 5 additions & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ type Twc<TCompose extends AbstractCompose> = (<T extends React.ElementType>(

type ShouldForwardProp = (prop: string) => boolean;

export type TwcComponentProps<
TComponent extends React.ElementType,
TCompose extends AbstractCompose = typeof clsx,
> = ResultProps<TComponent, undefined, { asChild?: boolean }, TCompose>;

export type Config<TCompose extends AbstractCompose> = {
/**
* The compose function to use. Defaults to `clsx`.
Expand Down
4 changes: 2 additions & 2 deletions website/components/Hero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ export function Hero() {
return (
<div className="text">
<div className="text-center mt-10 mb-20 max-w-4xl mx-auto text-balance">
<h1 className="text-[3.4rem] leading-none md:text-8xl mb-5 font-semibold font-accent text-transparent bg-clip-text bg-gradient-to-br from-blue-9 to-purple-11">
<h1 className="text-[3rem] leading-none md:text-8xl mb-5 font-semibold font-accent text-transparent bg-clip-text bg-gradient-to-br from-blue-9 to-purple-11">
Supercharge React + Tailwind
</h1>
<p className="text-xl md:text-2xl text-low">
<p className="text-lg md:text-2xl text-low">
TWC is a lightweight library to create Tailwind Components in one
line, write less code and build faster.
</p>
Expand Down
26 changes: 20 additions & 6 deletions website/pages/docs/guides/adapting-based-on-props.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,27 @@ You can pass a function to `twc` to adapt the components based on props.

This button component has a primary state that changes its color. When setting the `$primary` prop to true, we are changing its style.

```tsx
import { twc } from "react-twc";
```tsx {7} /props/ /$primary/
import { twc, TwcComponentProps } from "react-twc";

type ButtonProps = React.ComponentProps<"button"> & { $primary?: boolean };
type ButtonProps = TwcComponentProps<"button"> & { $primary?: boolean };

const Button = twc.button<ButtonProps>((props) => [
"font-semibold border border-blue-500 rounded",
props.$primary ? "bg-blue-500 text-white" : "bg-white text-gray-800",
]);

render(
export default () => (
<div>
<Button>Normal</Button>
<Button $primary>Primary</Button>
</div>,
);
```

You may have noticed the usage of `TwcComponentProps<"button">`, it is a helper to
return props accepted by a `twc` component. It's similar to `React.ComponentProps<"button">` with `asChild` prop and `className` type from `clsx`.

<details>
<summary>Why is the prop prefixed by a dollar?</summary>

Expand All @@ -35,7 +38,10 @@ We call the prop `$primary` a "transient prop", transient props can be consumed

For complex use cases, you can use [`cva`](https://cva.style/docs) with `twc` to have more control on the variants.

```tsx
```tsx {18} /$intent/
import { twc, TwcComponentProps } from "react-twc";
import { cva } from "class-variance-authority";

const button = cva("font-semibold border border-blue-500 rounded", {
variants: {
$intent: {
Expand All @@ -48,7 +54,15 @@ const button = cva("font-semibold border border-blue-500 rounded", {
},
});

type ButtonProps = React.ComponentProps<"button"> & VariantProps<typeof button>;
type ButtonProps = TwcComponentProps<"button"> & VariantProps<typeof button>;

const Button = twc.button<ButtonProps>(({ $intent }) => button({ $intent }));

export default () => (
<div>
<Button>Primary button (default)</Button>
<Button $intent="primary">Primary button</Button>
<Button $intent="secondary">Secondary button</Button>
</div>
);
```
30 changes: 16 additions & 14 deletions website/pages/docs/guides/as-child-prop.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,27 @@

## Usage

Take a `Link` component defined in your app:
A good example is an `Anchor` component that you may want to reuse but with a `next/link` component. Let's see how you can achieve this using `asChild`:

```ts filename="components/link.ts"
Take an `Anchor` component:

```ts filename="components/anchor.ts"
import { twc } from "react-twc";

const Link = twc.a`underline text-blue-800 hover:text-blue-900`;
export const Anchor = twc.a`underline text-blue-800 hover:text-blue-900`;
```

You can reuse the style for a `next/link`, it is possible using `asChild` prop:
You can reuse the style for a `next/link` using `asChild` prop:

```tsx filename="app.tsx"
```tsx {6} /asChild/
import NextLink from "next/link";
import { Link } from "./components/link";

function App() {
return (
<Link asChild>
<NextLink href="/">Go home</NextLink>
</Link>
);
}
import { Anchor } from "./components/anchor";

export default () => (
<Anchor asChild>
<NextLink href="/">Go home</NextLink>
</Anchor>,
);
```

To learn more about `asChild`, read [Radix documentation](https://www.radix-ui.com/primitives/docs/guides/composition#changing-the-element-type). `twc` uses [Slot component](https://www.radix-ui.com/primitives/docs/utilities/slot) internally to implement `asChild` prop.
4 changes: 2 additions & 2 deletions website/pages/docs/guides/class-name-prop.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const Title = twc.h2`text-2xl font-bold`;

By default classes are not merged. If you want to merge classes, you can use `twc` with [`tailwind-merge`](https://github.com/dcastil/tailwind-merge).

```ts filename="utils.ts"
```ts filename="utils.ts" {11}
import { twMerge } from "tailwind-merge";
import { clsx } from "clsx";
import { createTwc } from "react-twc";
Expand All @@ -42,7 +42,7 @@ export function cn(...inputs: ClassValue[]) {
export const twx = createTwc({ compose: cn });
```

```tsx filename="card.tsx"
```tsx filename="card.tsx" /twx/ {6}
import { twx } from "./utils";

const Card = twx.div`rounded-lg border bg-slate-100 text-white shadow-sm`;
Expand Down
37 changes: 37 additions & 0 deletions website/pages/docs/guides/refs.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Refs

One of the biggest feature of `twc` is the automatic forwarding of `ref` prop. You don't have to worry about it any more.

## Usage

Take this component with a forwarded ref.

```tsx /React.forwardRef/
import * as React from "react";

const Card = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className="rounded-lg border bg-slate-100 text-white shadow-sm"
{...props}
/>
));
```

With `twc`, the `ref` prop is automatically forwarded:

```tsx
const Card = twc.div`rounded-lg border bg-slate-100 text-white shadow-sm`;
```

So you can safely use `ref` on `Card` component:

```tsx /ref/
function CardDemo() {
const ref = useRef();
return <Card ref={ref}>TWC is magic!</Card>;
}
```
21 changes: 17 additions & 4 deletions website/pages/docs/guides/styling-any-component.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,24 @@

## Usage

In this example, we style a `next/link` by specifying `twc(Link)`:
Sometime you may want to style components from libraries like [Radix](https://www.radix-ui.com/) or [React Aria Components](https://react-spectrum.adobe.com/react-aria/index.html) or your own cooked components.

```tsx
import Link from "next/link";
In this example, we style a `HoverCard` from Radix:

```tsx {4-6,12}
import * as HoverCard from "@radix-ui/react-hover-card";
import { twc } from "react-twc";

const Link = twc(Link)`underline text-blue-800 hover:text-blue-900`;
const HoverCardContent = twc(
HoverCard.Content,
)`data-[side=bottom]:animate-slideUpAndFade data-[side=right]:animate-slideLeftAndFade data-[side=left]:animate-slideRightAndFade data-[side=top]:animate-slideDownAndFade w-[300px] rounded-md bg-white p-5 shadow-[hsl(206_22%_7%_/_35%)_0px_10px_38px_-10px,hsl(206_22%_7%_/_20%)_0px_10px_20px_-15px] data-[state=open]:transition-all`;

export default () => (
<HoverCard.Root>
<HoverCard.Trigger>Hover me</HoverCard.Trigger>
<HoverCard.Portal>
<HoverCardContent>Hello from hover card</HoverCardContent>
</HoverCard.Portal>
</HoverCard.Root>
);
```

0 comments on commit fafca5d

Please sign in to comment.