diff --git a/package.json b/package.json index b5c2d98..bcf3b38 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ }, "devDependencies": { "@ampproject/filesize": "^4.3.0", + "@radix-ui/react-accordion": "^1.1.2", "@swc/core": "^1.3.101", "@testing-library/react": "^14.1.2", "@types/react": "^18.2.42", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 38a540c..ddbf57a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,6 +16,9 @@ devDependencies: '@ampproject/filesize': specifier: ^4.3.0 version: 4.3.0 + '@radix-ui/react-accordion': + specifier: ^1.1.2 + version: 1.1.2(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) '@swc/core': specifier: ^1.3.101 version: 1.3.101 @@ -726,6 +729,90 @@ packages: fastq: 1.16.0 dev: true + /@radix-ui/primitive@1.0.1: + resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==} + dependencies: + '@babel/runtime': 7.23.5 + dev: true + + /@radix-ui/react-accordion@1.1.2(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-fDG7jcoNKVjSK6yfmuAs0EnPDro0WMXIhMtXdTBWqEioVW206ku+4Lw07e+13lUkFkpoEQ2PdeMIAGpdqEAmDg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-collapsible': 1.0.3(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-collection': 1.0.3(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@types/react': 18.2.42 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: true + + /@radix-ui/react-collapsible@1.0.3(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-presence': 1.0.1(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@types/react': 18.2.42 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: true + + /@radix-ui/react-collection@1.0.3(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.42)(react@18.2.0) + '@types/react': 18.2.42 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: true + /@radix-ui/react-compose-refs@1.0.1(@types/react@18.2.42)(react@18.2.0): resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==} peerDependencies: @@ -738,7 +825,90 @@ packages: '@babel/runtime': 7.23.5 '@types/react': 18.2.42 react: 18.2.0 - dev: false + + /@radix-ui/react-context@1.0.1(@types/react@18.2.42)(react@18.2.0): + resolution: {integrity: sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@types/react': 18.2.42 + react: 18.2.0 + dev: true + + /@radix-ui/react-direction@1.0.1(@types/react@18.2.42)(react@18.2.0): + resolution: {integrity: sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@types/react': 18.2.42 + react: 18.2.0 + dev: true + + /@radix-ui/react-id@1.0.1(@types/react@18.2.42)(react@18.2.0): + resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@types/react': 18.2.42 + react: 18.2.0 + dev: true + + /@radix-ui/react-presence@1.0.1(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@types/react': 18.2.42 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: true + + /@radix-ui/react-primitive@1.0.3(@types/react@18.2.42)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.42)(react@18.2.0) + '@types/react': 18.2.42 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: true /@radix-ui/react-slot@1.0.2(@types/react@18.2.42)(react@18.2.0): resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==} @@ -753,7 +923,49 @@ packages: '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.42)(react@18.2.0) '@types/react': 18.2.42 react: 18.2.0 - dev: false + + /@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.2.42)(react@18.2.0): + resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@types/react': 18.2.42 + react: 18.2.0 + dev: true + + /@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.2.42)(react@18.2.0): + resolution: {integrity: sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.42)(react@18.2.0) + '@types/react': 18.2.42 + react: 18.2.0 + dev: true + + /@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.2.42)(react@18.2.0): + resolution: {integrity: sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@babel/runtime': 7.23.5 + '@types/react': 18.2.42 + react: 18.2.0 + dev: true /@react-aria/breadcrumbs@3.5.9(react@18.2.0): resolution: {integrity: sha512-asbXTL5NjeHl1+YIF0K70y8tNHk8Lb6VneYH8yOkpLO49ejyNDYBK0tp0jtI9IZAQiTa2qkhYq58c9LloTwebQ==} diff --git a/src/index.test.tsx b/src/index.test.tsx index e49bf3b..e589d18 100644 --- a/src/index.test.tsx +++ b/src/index.test.tsx @@ -8,6 +8,7 @@ import { Button as AriaButton, ButtonProps as AriaButtonProps, } from "react-aria-components"; +import * as AccordionPrimitive from "@radix-ui/react-accordion"; describe("twc", () => { beforeEach(cleanup); @@ -226,4 +227,11 @@ describe("twc", () => { expect(button.classList.contains("bg-gray-700")).toBe(true); expect(button.classList.contains("opacity-35")).toBe(true); }); + + test("props are correctly typed", () => { + const Accordion = twc(AccordionPrimitive.Root)< + React.ComponentProps + >`py-2`; + render(); + }); }); diff --git a/src/index.tsx b/src/index.tsx index 9d47c60..291e4b5 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -100,7 +100,7 @@ function filterProps( type Attributes = Record | ((props: any) => Record); -export const createTwc = ( +export const createTwc = ( config: Config = {}, ) => { const compose = config.compose || clsx; diff --git a/website/pages/docs/getting-started.mdx b/website/pages/docs/getting-started.mdx index ae6caab..97173ba 100644 --- a/website/pages/docs/getting-started.mdx +++ b/website/pages/docs/getting-started.mdx @@ -10,7 +10,6 @@ import { Steps } from "nextra/components"; npm i react-twc ``` - ### Setup autocompletion in your editor You can enable Tailwind autocompletion inside `twc` using the steps below: @@ -26,7 +25,7 @@ You can enable Tailwind autocompletion inside `twc` using the steps below: { "tailwindCSS.experimental.classRegex": [ "twc\\.[^`]+`([^`]*)`", - "twc\\(.*?\\).*?`([^)]*)", + "twc\\(.*?\\).*?`([^`]*)", ["twc\\.[^`]+\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"], ["twc\\(.*?\\).*?\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"] ] @@ -47,7 +46,7 @@ You can enable Tailwind autocompletion inside `twc` using the steps below: experimental = { classRegex = { "twc\\.[^`]+`([^`]*)`", - "twc\\(.*?\\).*?`([^)]*)", + "twc\\(.*?\\).*?`([^`]*)", { "twc\\.[^`]+\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)" }, { "twc\\(.*?\\).*?\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)" } }, @@ -71,7 +70,7 @@ You can enable Tailwind autocompletion inside `twc` using the steps below: "experimental": { "classRegex": [ "twc\\.[^`]+`([^`]*)`", - "twc\\(.*?\\).*?`([^)]*)", + "twc\\(.*?\\).*?`([^`]*)", ["twc\\.[^`]+\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"], ["twc\\(.*?\\).*?\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"] ] diff --git a/website/pages/docs/guides/styling-any-component.mdx b/website/pages/docs/guides/styling-any-component.mdx index 8df763b..b2fc217 100644 --- a/website/pages/docs/guides/styling-any-component.mdx +++ b/website/pages/docs/guides/styling-any-component.mdx @@ -33,3 +33,25 @@ const HoverCardContent = twc(HoverCard.Content).attrs({ sideOffset: 5, })`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`; ``` + +## Specify props + +When you wrap a component with `twc`, it infers the component's props automatically. However, this can sometimes result in unpredictable behavior. In such scenarios, it's better to define the props manually. + +For example, with the [`@radix-ui/react-accordion`](https://www.radix-ui.com/primitives/docs/components/accordion#accordion) component, its complex props like `type` and `collapsible` might not be inferred correctly by twc. To prevent issues, explicitly declare these props when using `twc`. This explicit declaration ensures accurate prop handling, avoiding potential problems. + +```tsx +import * as React from 'react' +import * as AccordionPrimitive from "@radix-ui/react-accordion"; +import { twc } from "react-twc"; + +const Accordion = twc(AccordionPrimitive.Root)>`py-2`; + +export default () => { + return ( + {/* This work without any type error, `collapsible` is authorized when `type="single"` */} + + + ); +} +```