react-native-twc 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +283 -0
- package/dist/index.d.ts +101 -0
- package/dist/index.js +65 -0
- package/package.json +80 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023-Present Greg Bergé
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
# react-native-twc
|
|
2
|
+
|
|
3
|
+
> Create reusable React Native + NativeWind components with Tailwind CSS syntax
|
|
4
|
+
|
|
5
|
+
This project is a **React Native adaptation** of [TWC (react-twc)](https://github.com/gregberge/twc) by [Greg Bergé](https://github.com/gregberge). The original TWC library provides an elegant way to create styled React components with Tailwind CSS. This fork adapts the library specifically for React Native with NativeWind support.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- ⚡️ **Lightweight** — only ~1KB minified
|
|
10
|
+
- ✨ **Full TypeScript support** with autocompletion
|
|
11
|
+
- 🎨 **Dynamic styling** based on props
|
|
12
|
+
- 🦄 **Works with any React Native component**
|
|
13
|
+
- 🚀 **First-class `tailwind-merge` and `cva` support**
|
|
14
|
+
- 📱 **Built for React Native + NativeWind**
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# npm
|
|
20
|
+
npm install react-native-twc
|
|
21
|
+
|
|
22
|
+
# yarn
|
|
23
|
+
yarn add react-native-twc
|
|
24
|
+
|
|
25
|
+
# bun
|
|
26
|
+
bun add react-native-twc
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Peer Dependencies
|
|
30
|
+
|
|
31
|
+
Make sure you have the following peer dependencies installed:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install react react-native nativewind tailwind-merge
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
### Basic Usage
|
|
40
|
+
|
|
41
|
+
**Without `twc`:**
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
import * as React from "react";
|
|
45
|
+
import { View, Text, ViewProps } from "react-native";
|
|
46
|
+
import clsx from "clsx";
|
|
47
|
+
|
|
48
|
+
const Card = React.forwardRef<View, ViewProps>(({ className, ...props }, ref) => (
|
|
49
|
+
<View
|
|
50
|
+
ref={ref}
|
|
51
|
+
className={clsx("rounded-lg border bg-slate-100 p-4 shadow-sm", className)}
|
|
52
|
+
{...props}
|
|
53
|
+
/>
|
|
54
|
+
));
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**With `twc`:**
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
import { View } from "react-native";
|
|
61
|
+
import { twc } from "react-native-twc";
|
|
62
|
+
|
|
63
|
+
const Card = twc(View)`rounded-lg border bg-slate-100 p-4 shadow-sm`;
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Creating Styled Components
|
|
67
|
+
|
|
68
|
+
```tsx
|
|
69
|
+
import { View, Text, Pressable, TextInput } from "react-native";
|
|
70
|
+
import { twc } from "react-native-twc";
|
|
71
|
+
|
|
72
|
+
// Basic styled components
|
|
73
|
+
const Container = twc(View)`flex-1 bg-white p-4`;
|
|
74
|
+
const Title = twc(Text)`text-2xl font-bold text-gray-900`;
|
|
75
|
+
const Subtitle = twc(Text)`text-lg text-gray-600`;
|
|
76
|
+
|
|
77
|
+
// Styled input
|
|
78
|
+
const Input = twc(TextInput)`border border-gray-300 rounded-lg px-4 py-2`;
|
|
79
|
+
|
|
80
|
+
// Styled button
|
|
81
|
+
const Button = twc(Pressable)`bg-blue-500 py-3 px-6 rounded-lg`;
|
|
82
|
+
const ButtonText = twc(Text)`text-white font-semibold text-center`;
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Dynamic Styling with Props
|
|
86
|
+
|
|
87
|
+
Use the `$` prefix for transient props that won't be passed to the underlying component:
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
import { Pressable, Text } from "react-native";
|
|
91
|
+
import { twc, TwcComponentProps } from "react-native-twc";
|
|
92
|
+
|
|
93
|
+
type ButtonProps = TwcComponentProps<typeof Pressable> & {
|
|
94
|
+
$variant?: "primary" | "secondary" | "danger";
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const Button = twc(Pressable)<ButtonProps>((props) => [
|
|
98
|
+
"py-3 px-6 rounded-lg font-semibold",
|
|
99
|
+
{
|
|
100
|
+
"bg-blue-500": props.$variant === "primary",
|
|
101
|
+
"bg-gray-200": props.$variant === "secondary",
|
|
102
|
+
"bg-red-500": props.$variant === "danger",
|
|
103
|
+
},
|
|
104
|
+
]);
|
|
105
|
+
|
|
106
|
+
// Usage
|
|
107
|
+
<Button $variant="primary">
|
|
108
|
+
<Text className="text-white">Click me</Text>
|
|
109
|
+
</Button>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Using with `attrs`
|
|
113
|
+
|
|
114
|
+
Add default props to your components:
|
|
115
|
+
|
|
116
|
+
```tsx
|
|
117
|
+
import { TextInput } from "react-native";
|
|
118
|
+
import { twc } from "react-native-twc";
|
|
119
|
+
|
|
120
|
+
const EmailInput = twc(TextInput).attrs({
|
|
121
|
+
keyboardType: "email-address",
|
|
122
|
+
autoCapitalize: "none",
|
|
123
|
+
placeholder: "Enter your email",
|
|
124
|
+
})`border border-gray-300 rounded-lg px-4 py-2`;
|
|
125
|
+
|
|
126
|
+
// Usage
|
|
127
|
+
<EmailInput onChangeText={(text) => console.log(text)} />
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Using with CVA (Class Variance Authority)
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
import { Pressable } from "react-native";
|
|
134
|
+
import { cva, VariantProps } from "class-variance-authority";
|
|
135
|
+
import { twc, TwcComponentProps } from "react-native-twc";
|
|
136
|
+
|
|
137
|
+
const buttonVariants = cva("rounded-lg font-semibold", {
|
|
138
|
+
variants: {
|
|
139
|
+
$intent: {
|
|
140
|
+
primary: "bg-blue-500 text-white",
|
|
141
|
+
secondary: "bg-gray-200 text-gray-800",
|
|
142
|
+
danger: "bg-red-500 text-white",
|
|
143
|
+
},
|
|
144
|
+
$size: {
|
|
145
|
+
sm: "py-1 px-2 text-sm",
|
|
146
|
+
md: "py-2 px-4 text-base",
|
|
147
|
+
lg: "py-3 px-6 text-lg",
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
defaultVariants: {
|
|
151
|
+
$intent: "primary",
|
|
152
|
+
$size: "md",
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
type ButtonProps = TwcComponentProps<typeof Pressable> &
|
|
157
|
+
VariantProps<typeof buttonVariants>;
|
|
158
|
+
|
|
159
|
+
const Button = twc(Pressable)<ButtonProps>(({ $intent, $size }) =>
|
|
160
|
+
buttonVariants({ $intent, $size })
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
// Usage
|
|
164
|
+
<Button $intent="danger" $size="lg">
|
|
165
|
+
<Text>Delete</Text>
|
|
166
|
+
</Button>
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Using with Tailwind Merge
|
|
170
|
+
|
|
171
|
+
Use the built-in `twx` for automatic class conflict resolution:
|
|
172
|
+
|
|
173
|
+
```tsx
|
|
174
|
+
import { Text } from "react-native";
|
|
175
|
+
import { twx } from "react-native-twc";
|
|
176
|
+
|
|
177
|
+
const Title = twx(Text)`font-bold text-lg`;
|
|
178
|
+
|
|
179
|
+
// Later classes override earlier ones
|
|
180
|
+
<Title className="font-normal text-sm">Hello</Title>
|
|
181
|
+
// Result: "font-normal text-sm" (conflicts resolved)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Or create your own instance:
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
import { twMerge } from "tailwind-merge";
|
|
188
|
+
import { createTwc } from "react-native-twc";
|
|
189
|
+
|
|
190
|
+
const twc = createTwc({ compose: twMerge });
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Custom Transient Props
|
|
194
|
+
|
|
195
|
+
By default, props starting with `$` are not forwarded. Customize this behavior:
|
|
196
|
+
|
|
197
|
+
```tsx
|
|
198
|
+
import { View } from "react-native";
|
|
199
|
+
import { twc, TwcComponentProps } from "react-native-twc";
|
|
200
|
+
|
|
201
|
+
type Props = TwcComponentProps<typeof View> & { size: "sm" | "lg" };
|
|
202
|
+
|
|
203
|
+
const Box = twc(View).transientProps(["size"])<Props>((props) => ({
|
|
204
|
+
"w-4 h-4": props.size === "sm",
|
|
205
|
+
"w-8 h-8": props.size === "lg",
|
|
206
|
+
}));
|
|
207
|
+
|
|
208
|
+
// 'size' won't be passed to the underlying View
|
|
209
|
+
<Box size="lg" />
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## API Reference
|
|
213
|
+
|
|
214
|
+
### `twc(Component)`
|
|
215
|
+
|
|
216
|
+
Wraps a React Native component and returns a template function.
|
|
217
|
+
|
|
218
|
+
```tsx
|
|
219
|
+
const StyledView = twc(View)`bg-white p-4`;
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### `createTwc(config)`
|
|
223
|
+
|
|
224
|
+
Creates a custom TWC instance with configuration options.
|
|
225
|
+
|
|
226
|
+
```tsx
|
|
227
|
+
const twc = createTwc({
|
|
228
|
+
compose: twMerge, // Custom class merging function
|
|
229
|
+
shouldForwardProp: (prop) => !prop.startsWith("_"), // Custom prop filtering
|
|
230
|
+
});
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### `TwcComponentProps<T>`
|
|
234
|
+
|
|
235
|
+
Utility type to extract props from a TWC-wrapped component.
|
|
236
|
+
|
|
237
|
+
```tsx
|
|
238
|
+
type CardProps = TwcComponentProps<typeof View>;
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### `cn(...inputs)`
|
|
242
|
+
|
|
243
|
+
Utility function combining `clsx` and `tailwind-merge`.
|
|
244
|
+
|
|
245
|
+
```tsx
|
|
246
|
+
import { cn } from "react-native-twc";
|
|
247
|
+
|
|
248
|
+
const className = cn("p-4", condition && "bg-blue-500", ["rounded", "shadow"]);
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### `twx`
|
|
252
|
+
|
|
253
|
+
Pre-configured TWC instance using `tailwind-merge` for class conflict resolution.
|
|
254
|
+
|
|
255
|
+
```tsx
|
|
256
|
+
import { twx } from "react-native-twc";
|
|
257
|
+
|
|
258
|
+
const Title = twx(Text)`font-bold`;
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Differences from Original TWC
|
|
262
|
+
|
|
263
|
+
| Feature | Original TWC | react-native-twc |
|
|
264
|
+
|---------|--------------|------------------|
|
|
265
|
+
| HTML tags (`twc.div`) | ✅ Supported | ❌ Not supported |
|
|
266
|
+
| `asChild` prop | ✅ Supported | ❌ Not supported |
|
|
267
|
+
| React Native components | ❌ Not optimized | ✅ Fully supported |
|
|
268
|
+
| NativeWind | ❌ Not designed for | ✅ First-class support |
|
|
269
|
+
|
|
270
|
+
## Acknowledgements
|
|
271
|
+
|
|
272
|
+
- [TWC (react-twc)](https://github.com/gregberge/twc) by [Greg Bergé](https://github.com/gregberge) - The original inspiration and foundation for this project
|
|
273
|
+
- [NativeWind](https://www.nativewind.dev/) - Tailwind CSS for React Native
|
|
274
|
+
- [styled-components](https://styled-components.com) - Where the template literal API originated
|
|
275
|
+
- [tailwind-merge](https://github.com/dcastil/tailwind-merge) - Intelligent Tailwind class merging
|
|
276
|
+
|
|
277
|
+
## License
|
|
278
|
+
|
|
279
|
+
MIT License © 2023-Present [Greg Bergé](https://github.com/gregberge)
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
**Note:** This is a community fork adapted for React Native. For the original React (web) version, please visit [react-twc](https://github.com/gregberge/twc).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { type ClassValue, clsx } from 'clsx';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
export { clsx as cx };
|
|
4
|
+
type AbstractCompose = (...params: any) => any;
|
|
5
|
+
/**
|
|
6
|
+
* React Native 组件类型定义
|
|
7
|
+
* 支持任何接受 className 或 style prop 的 React Native 组件
|
|
8
|
+
*/
|
|
9
|
+
type RNComponentType = React.ComponentType<any>;
|
|
10
|
+
/**
|
|
11
|
+
* 获取组件的 Props 类型
|
|
12
|
+
*/
|
|
13
|
+
type ComponentProps<T> = T extends React.ComponentType<infer P> ? P : never;
|
|
14
|
+
/**
|
|
15
|
+
* 结果 Props 类型
|
|
16
|
+
* 根据泛型参数组合最终的 props 类型
|
|
17
|
+
*/
|
|
18
|
+
type ResultProps<TComponent extends RNComponentType, TProps, TExtraProps, TCompose extends AbstractCompose> = TProps extends undefined ? TExtraProps extends undefined ? Omit<ComponentProps<TComponent>, 'className'> & {
|
|
19
|
+
className?: Parameters<TCompose>[0];
|
|
20
|
+
} : Omit<ComponentProps<TComponent>, 'className'> & {
|
|
21
|
+
className?: Parameters<TCompose>[0];
|
|
22
|
+
} & TExtraProps : TProps;
|
|
23
|
+
/**
|
|
24
|
+
* 模板函数类型
|
|
25
|
+
* 支持模板字符串语法和函数形式
|
|
26
|
+
*/
|
|
27
|
+
type Template<TComponent extends RNComponentType, TCompose extends AbstractCompose, TExtraProps, TParentProps = undefined> = <TProps = TParentProps>(strings: TemplateStringsArray | ((props: ResultProps<TComponent, TProps, TExtraProps, TCompose>) => 'className' extends keyof TProps ? TProps['className'] : Parameters<TCompose>[0]), ...values: any[]) => React.ForwardRefExoticComponent<ResultProps<TComponent, TProps, TExtraProps, TCompose>>;
|
|
28
|
+
/**
|
|
29
|
+
* 第一级模板类型
|
|
30
|
+
* 包含 attrs 和 transientProps 方法
|
|
31
|
+
*/
|
|
32
|
+
type FirstLevelTemplate<TComponent extends RNComponentType, TCompose extends AbstractCompose, TExtraProps> = Template<TComponent, TCompose, TExtraProps> & {
|
|
33
|
+
/**
|
|
34
|
+
* 为组件添加额外的默认 props
|
|
35
|
+
* @example
|
|
36
|
+
* const TextInput = twc(RNTextInput).attrs({ keyboardType: 'email-address' })`...`
|
|
37
|
+
*/
|
|
38
|
+
attrs: <TProps = undefined>(attrs: (Omit<Partial<ComponentProps<TComponent>>, 'className'> & Record<string, any>) | ((props: ResultProps<TComponent, TProps, TExtraProps, TCompose>) => Record<string, any>)) => Template<TComponent, TCompose, TExtraProps, TProps>;
|
|
39
|
+
} & {
|
|
40
|
+
/**
|
|
41
|
+
* 防止特定 props 被转发到底层组件
|
|
42
|
+
* @example
|
|
43
|
+
* const Title = twc(Text).transientProps(['$size'])`...`
|
|
44
|
+
*/
|
|
45
|
+
transientProps: (fn: string[] | ((prop: string) => boolean)) => FirstLevelTemplate<TComponent, TCompose, TExtraProps>;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* TWC 主类型
|
|
49
|
+
* 只支持传入 React Native 组件,不支持字符串标签
|
|
50
|
+
*/
|
|
51
|
+
type Twc<TCompose extends AbstractCompose> = <T extends RNComponentType>(component: T) => FirstLevelTemplate<T, TCompose, undefined>;
|
|
52
|
+
/**
|
|
53
|
+
* TWC 组件 Props 工具类型
|
|
54
|
+
* 用于获取 TWC 组件的 props 类型
|
|
55
|
+
*/
|
|
56
|
+
export type TwcComponentProps<TComponent extends RNComponentType, TCompose extends AbstractCompose = typeof clsx> = ResultProps<TComponent, undefined, undefined, TCompose>;
|
|
57
|
+
/**
|
|
58
|
+
* 配置类型
|
|
59
|
+
*/
|
|
60
|
+
export type Config<TCompose extends AbstractCompose> = {
|
|
61
|
+
/**
|
|
62
|
+
* className 合并函数,默认使用 clsx
|
|
63
|
+
* 可以替换为 tailwind-merge 等库
|
|
64
|
+
*/
|
|
65
|
+
compose?: TCompose;
|
|
66
|
+
/**
|
|
67
|
+
* 判断 prop 是否应该转发到底层组件的函数
|
|
68
|
+
* 默认规则: prop => prop[0] !== "$" (以 $ 开头的不转发)
|
|
69
|
+
*/
|
|
70
|
+
shouldForwardProp?: (prop: string) => boolean;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* 创建 TWC 实例的工厂函数
|
|
74
|
+
*
|
|
75
|
+
* @param config - 配置选项
|
|
76
|
+
* @returns TWC 实例
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* // 基础使用
|
|
80
|
+
* import { View, Text } from 'react-native';
|
|
81
|
+
* const twc = createTwc();
|
|
82
|
+
* const Card = twc(View)`bg-white rounded-lg p-4`;
|
|
83
|
+
* const Title = twc(Text)`text-xl font-bold`;
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* // 使用 tailwind-merge
|
|
87
|
+
* import { twMerge } from 'tailwind-merge';
|
|
88
|
+
* const twc = createTwc({ compose: twMerge });
|
|
89
|
+
*/
|
|
90
|
+
export declare const createTwc: <TCompose extends AbstractCompose = typeof clsx>(config?: Config<TCompose>) => Twc<TCompose>;
|
|
91
|
+
/**
|
|
92
|
+
* 默认的 TWC 实例
|
|
93
|
+
* 使用 clsx 作为 compose 函数
|
|
94
|
+
*/
|
|
95
|
+
export declare const twc: Twc<typeof clsx>;
|
|
96
|
+
export declare const cn: (...inputs: ClassValue[]) => string;
|
|
97
|
+
/**
|
|
98
|
+
* 带tailwind-merge的 TWC 实例
|
|
99
|
+
* 使用 cn 作为 compose 函数
|
|
100
|
+
*/
|
|
101
|
+
export declare const twx: Twc<(...inputs: ClassValue[]) => string>;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// src/index.tsx
|
|
2
|
+
import { clsx } from "clsx";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { twMerge } from "tailwind-merge";
|
|
5
|
+
import { jsxDEV } from "react/jsx-dev-runtime";
|
|
6
|
+
function filterProps(props, shouldForwardProp) {
|
|
7
|
+
const filteredProps = {};
|
|
8
|
+
const keys = Object.keys(props);
|
|
9
|
+
for (let i = 0;i < keys.length; i++) {
|
|
10
|
+
const prop = keys[i];
|
|
11
|
+
if (shouldForwardProp(prop)) {
|
|
12
|
+
filteredProps[prop] = props[prop];
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return filteredProps;
|
|
16
|
+
}
|
|
17
|
+
var createTwc = (config = {}) => {
|
|
18
|
+
const compose = config.compose || clsx;
|
|
19
|
+
const defaultShouldForwardProp = config.shouldForwardProp || ((prop) => prop[0] !== "$");
|
|
20
|
+
const wrap = (Component) => {
|
|
21
|
+
const createTemplate = (attrs, shouldForwardProp = defaultShouldForwardProp) => {
|
|
22
|
+
const template = (stringsOrFn, ...values) => {
|
|
23
|
+
const isClassFn = typeof stringsOrFn === "function";
|
|
24
|
+
const tplClassName = !isClassFn && String.raw({ raw: stringsOrFn }, ...values);
|
|
25
|
+
const ForwardedComponent = React.forwardRef((p, ref) => {
|
|
26
|
+
const { className: classNameProp, ...rest } = p;
|
|
27
|
+
const resolvedAttrs = typeof attrs === "function" ? attrs(p) : attrs ? attrs : {};
|
|
28
|
+
const filteredProps = filterProps({ ...resolvedAttrs, ...rest }, shouldForwardProp);
|
|
29
|
+
const baseClassName = isClassFn ? stringsOrFn(p) : tplClassName;
|
|
30
|
+
const finalClassName = typeof baseClassName === "function" ? (renderProps) => compose(baseClassName(renderProps), typeof classNameProp === "function" ? classNameProp(renderProps) : classNameProp) : compose(baseClassName, classNameProp);
|
|
31
|
+
const Comp = Component;
|
|
32
|
+
return /* @__PURE__ */ jsxDEV(Comp, {
|
|
33
|
+
ref,
|
|
34
|
+
className: finalClassName,
|
|
35
|
+
...filteredProps
|
|
36
|
+
}, undefined, false, undefined, this);
|
|
37
|
+
});
|
|
38
|
+
ForwardedComponent.displayName = `twc(${Component.displayName || Component.name || "Component"})`;
|
|
39
|
+
return ForwardedComponent;
|
|
40
|
+
};
|
|
41
|
+
template.transientProps = (fnOrArray) => {
|
|
42
|
+
const shouldForwardProp2 = typeof fnOrArray === "function" ? (prop) => !fnOrArray(prop) : (prop) => !fnOrArray.includes(prop);
|
|
43
|
+
return createTemplate(attrs, shouldForwardProp2);
|
|
44
|
+
};
|
|
45
|
+
if (attrs === undefined) {
|
|
46
|
+
template.attrs = (attrs2) => {
|
|
47
|
+
return createTemplate(attrs2, shouldForwardProp);
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return template;
|
|
51
|
+
};
|
|
52
|
+
return createTemplate();
|
|
53
|
+
};
|
|
54
|
+
return wrap;
|
|
55
|
+
};
|
|
56
|
+
var twc = createTwc();
|
|
57
|
+
var cn = (...inputs) => twMerge(clsx(inputs));
|
|
58
|
+
var twx = createTwc({ compose: cn });
|
|
59
|
+
export {
|
|
60
|
+
twx,
|
|
61
|
+
twc,
|
|
62
|
+
clsx as cx,
|
|
63
|
+
createTwc,
|
|
64
|
+
cn
|
|
65
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-native-twc",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Create reusable React Native + NativeWind components with Tailwind CSS syntax.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"import": "./dist/index.js",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"scripts": {
|
|
15
|
+
"test": "bun test",
|
|
16
|
+
"build": "rm -rf dist && bun run build:js && bun run build:dts",
|
|
17
|
+
"build:js": "bun build src/index.tsx --outdir dist --format esm --external react --external react-native --external clsx --external tailwind-merge",
|
|
18
|
+
"build:dts": "tsc -p tsconfig.build.json",
|
|
19
|
+
"check-types": "tsc --noEmit",
|
|
20
|
+
"publish": "bun run build && npm publish --access public"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"react-native",
|
|
24
|
+
"nativewind",
|
|
25
|
+
"tailwind",
|
|
26
|
+
"css",
|
|
27
|
+
"components",
|
|
28
|
+
"styled",
|
|
29
|
+
"styled-components",
|
|
30
|
+
"expo"
|
|
31
|
+
],
|
|
32
|
+
"author": "Greg Bergé",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "git+https://github.com/gregberge/twc.git"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://react-twc.vercel.app",
|
|
39
|
+
"funding": {
|
|
40
|
+
"type": "github",
|
|
41
|
+
"url": "https://github.com/sponsors/gregberge"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@eslint/eslintrc": "^3.3.1",
|
|
45
|
+
"@eslint/js": "^9.30.0",
|
|
46
|
+
"@happy-dom/global-registrator": "^18.0.1",
|
|
47
|
+
"@testing-library/react": "^16.3.0",
|
|
48
|
+
"@types/bun": "^1.2.5",
|
|
49
|
+
"@types/react": "^19.1.8",
|
|
50
|
+
"@typescript-eslint/eslint-plugin": "^8.35.0",
|
|
51
|
+
"@typescript-eslint/parser": "^8.35.0",
|
|
52
|
+
"class-variance-authority": "^0.7.1",
|
|
53
|
+
"eslint": "^9.30.0",
|
|
54
|
+
"happy-dom": "^18.0.1",
|
|
55
|
+
"prettier": "^3.6.2",
|
|
56
|
+
"react": "^19.1.0",
|
|
57
|
+
"react-dom": "^19.1.0",
|
|
58
|
+
"tailwind-merge": "^3.3.1",
|
|
59
|
+
"typescript": "^5.8.3",
|
|
60
|
+
"typescript-eslint": "^8.35.0",
|
|
61
|
+
"ultracite": "7.0.10"
|
|
62
|
+
},
|
|
63
|
+
"dependencies": {
|
|
64
|
+
"clsx": "^2.1.1"
|
|
65
|
+
},
|
|
66
|
+
"peerDependencies": {
|
|
67
|
+
"react": ">=18.0.0",
|
|
68
|
+
"react-native": ">=0.72.0",
|
|
69
|
+
"nativewind": ">=4.0.0",
|
|
70
|
+
"tailwind-merge": ">=3.3.1"
|
|
71
|
+
},
|
|
72
|
+
"peerDependenciesMeta": {
|
|
73
|
+
"react-native": {
|
|
74
|
+
"optional": true
|
|
75
|
+
},
|
|
76
|
+
"nativewind": {
|
|
77
|
+
"optional": true
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|