@niibase/uniwind 1.4.0 → 1.5.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.
Files changed (63) hide show
  1. package/dist/common/common/consts.js +18 -0
  2. package/dist/common/components/native/Pressable.js +13 -2
  3. package/dist/common/components/native/TouchableHighlight.js +11 -1
  4. package/dist/common/components/native/TouchableOpacity.js +11 -1
  5. package/dist/common/core/native/runtime.js +11 -1
  6. package/dist/common/core/native/store.js +19 -2
  7. package/dist/common/css/variants.js +1 -1
  8. package/dist/common/css-visitor/function-visitor.js +9 -0
  9. package/dist/common/css-visitor/rule-visitor.js +49 -5
  10. package/dist/metro/index.cjs +2 -2
  11. package/dist/metro/index.d.ts +1 -0
  12. package/dist/metro/index.mjs +1 -1
  13. package/dist/metro/metro-transformer.cjs +47 -11
  14. package/dist/metro/metro-transformer.mjs +45 -9
  15. package/dist/module/common/consts.d.ts +11 -0
  16. package/dist/module/common/consts.js +12 -0
  17. package/dist/module/components/native/Pressable.d.ts +5 -0
  18. package/dist/module/components/native/Pressable.js +12 -2
  19. package/dist/module/components/native/TouchableHighlight.js +11 -1
  20. package/dist/module/components/native/TouchableOpacity.js +11 -1
  21. package/dist/module/core/native/parsers/gradient.d.ts +1 -1
  22. package/dist/module/core/native/runtime.js +5 -2
  23. package/dist/module/core/native/store.d.ts +1 -0
  24. package/dist/module/core/native/store.js +19 -2
  25. package/dist/module/core/types.d.ts +1 -0
  26. package/dist/module/css/variants.js +1 -1
  27. package/dist/module/css-visitor/function-visitor.d.ts +1 -0
  28. package/dist/module/css-visitor/function-visitor.js +3 -0
  29. package/dist/module/css-visitor/rule-visitor.d.ts +3 -1
  30. package/dist/module/css-visitor/rule-visitor.js +54 -5
  31. package/dist/shared/uniwind.B5q8hBGv.cjs +18 -0
  32. package/dist/shared/{uniwind.D-ahjOrG.mjs → uniwind.BWb5KNML.mjs} +58 -6
  33. package/dist/shared/{uniwind.CyACT0sD.cjs → uniwind.DTMo4epG.cjs} +58 -6
  34. package/dist/shared/uniwind.JSWK3vHl.mjs +14 -0
  35. package/dist/vite/index.cjs +1 -1
  36. package/dist/vite/index.mjs +1 -1
  37. package/package.json +19 -13
  38. package/readme.md +4 -12
  39. package/src/common/consts.ts +12 -0
  40. package/src/components/native/Pressable.tsx +17 -1
  41. package/src/components/native/TouchableHighlight.tsx +10 -0
  42. package/src/components/native/TouchableOpacity.tsx +10 -0
  43. package/src/core/config/config.common.ts +1 -2
  44. package/src/core/native/parsers/gradient.ts +1 -1
  45. package/src/core/native/runtime.ts +5 -1
  46. package/src/core/native/store.ts +24 -2
  47. package/src/core/types.ts +1 -0
  48. package/src/css/variants.ts +1 -1
  49. package/src/css-visitor/function-visitor.ts +4 -0
  50. package/src/css-visitor/rule-visitor.ts +68 -9
  51. package/src/metro/addMetaToStylesTemplate.ts +9 -3
  52. package/src/metro/compileVirtual.ts +2 -1
  53. package/src/metro/index.d.ts +1 -0
  54. package/src/metro/metro-transformer.ts +19 -2
  55. package/src/metro/processor/functions.ts +4 -0
  56. package/src/metro/processor/mq.ts +12 -2
  57. package/src/metro/processor/processor.ts +3 -2
  58. package/src/metro/processor/rn.ts +10 -0
  59. package/src/metro/types.ts +2 -7
  60. package/src/metro/withUniwindConfig.ts +2 -1
  61. package/uniwind.css +5 -2
  62. package/dist/shared/uniwind.BZIuaszw.cjs +0 -11
  63. package/dist/shared/uniwind.CyoRUwOj.mjs +0 -9
@@ -5,9 +5,11 @@ import { copyComponentProperties } from "../utils.js";
5
5
  import { useStyle } from "./useStyle.js";
6
6
  export const TouchableHighlight = copyComponentProperties(RNTouchableHighlight, (props) => {
7
7
  const [isPressed, setIsPressed] = useState(false);
8
+ const [isFocused, setIsFocused] = useState(false);
8
9
  const state = {
9
10
  isDisabled: Boolean(props.disabled),
10
- isPressed
11
+ isPressed,
12
+ isFocused
11
13
  };
12
14
  const { Component, style } = useStyle(RNTouchableHighlight, props.className, props, state);
13
15
  const underlayColor = useStyle(props.underlayColorClassName, props, state).accentColor;
@@ -24,6 +26,14 @@ export const TouchableHighlight = copyComponentProperties(RNTouchableHighlight,
24
26
  onPressOut: (event) => {
25
27
  setIsPressed(false);
26
28
  props.onPressOut?.(event);
29
+ },
30
+ onFocus: (event) => {
31
+ setIsFocused(true);
32
+ props.onFocus?.(event);
33
+ },
34
+ onBlur: (event) => {
35
+ setIsFocused(false);
36
+ props.onBlur?.(event);
27
37
  }
28
38
  }
29
39
  );
@@ -5,9 +5,11 @@ import { copyComponentProperties } from "../utils.js";
5
5
  import { useStyle } from "./useStyle.js";
6
6
  export const TouchableOpacity = copyComponentProperties(RNTouchableOpacity, (props) => {
7
7
  const [isPressed, setIsPressed] = useState(false);
8
+ const [isFocused, setIsFocused] = useState(false);
8
9
  const state = {
9
10
  isDisabled: Boolean(props.disabled),
10
- isPressed
11
+ isPressed,
12
+ isFocused
11
13
  };
12
14
  const { Component, style } = useStyle(RNTouchableOpacity, props.className, props, state);
13
15
  return /* @__PURE__ */ jsx(
@@ -22,6 +24,14 @@ export const TouchableOpacity = copyComponentProperties(RNTouchableOpacity, (pro
22
24
  onPressOut: (event) => {
23
25
  setIsPressed(false);
24
26
  props.onPressOut?.(event);
27
+ },
28
+ onFocus: (event) => {
29
+ setIsFocused(true);
30
+ props.onFocus?.(event);
31
+ },
32
+ onBlur: (event) => {
33
+ setIsFocused(false);
34
+ props.onBlur?.(event);
25
35
  }
26
36
  }
27
37
  );
@@ -1,7 +1,7 @@
1
1
  export declare const resolveGradient: (value: string) => {
2
2
  colorStops: {
3
3
  color: string;
4
- positions: string[][] | undefined;
4
+ positions: string[] | undefined;
5
5
  }[];
6
6
  type: "linear-gradient";
7
7
  direction: string | undefined;
@@ -1,4 +1,4 @@
1
- import { Appearance, Dimensions, I18nManager, PixelRatio, StyleSheet } from "react-native";
1
+ import { Appearance, Dimensions, I18nManager, PixelRatio, Platform, StyleSheet } from "react-native";
2
2
  import { cubicBezier, linear, steps } from "react-native-reanimated";
3
3
  import { initialWindowMetrics } from "react-native-safe-area-context";
4
4
  import { ColorScheme, Orientation } from "../../types.js";
@@ -23,6 +23,9 @@ export const UniwindRuntime = {
23
23
  steps,
24
24
  linear,
25
25
  lightDark: () => "",
26
- parseColor
26
+ parseColor,
27
+ platformSelect: (android, ios, other) => {
28
+ return Platform.select(Boolean(other) ? { android, ios, default: other } : { android, default: ios });
29
+ }
27
30
  };
28
31
  UniwindRuntime.lightDark = lightDark.bind(UniwindRuntime);
@@ -15,6 +15,7 @@ declare class UniwindStoreBuilder {
15
15
  reinit: (generateStyleSheetCallback: GenerateStyleSheetsCallback, themes: Array<string>) => void;
16
16
  private resolveStyles;
17
17
  private validateDataAttributes;
18
+ private getCurrentPlatform;
18
19
  }
19
20
  export declare const UniwindStore: UniwindStoreBuilder;
20
21
  export {};
@@ -1,4 +1,5 @@
1
1
  import { Dimensions, Platform } from "react-native";
2
+ import { Platform as UniwindPlatform, UNIWIND_PLATFORM_VARIABLES, UNIWIND_THEME_VARIABLES } from "../../common/consts.js";
2
3
  import { Orientation, StyleDependency } from "../../types.js";
3
4
  import { UniwindListener } from "../listener.js";
4
5
  import { cloneWithAccessors } from "./native-utils.js";
@@ -40,7 +41,13 @@ class UniwindStoreBuilder {
40
41
  }
41
42
  reinit = (generateStyleSheetCallback, themes) => {
42
43
  const { scopedVars, stylesheet, vars, keyframes } = generateStyleSheetCallback(this.runtime);
43
- const platformVars = scopedVars[`__uniwind-platform-${Platform.OS}`];
44
+ const platform = this.getCurrentPlatform();
45
+ const commonPlatform = platform.includes("tv") ? UniwindPlatform.TV : UniwindPlatform.Native;
46
+ const commonPlatformVars = scopedVars[`${UNIWIND_PLATFORM_VARIABLES}${commonPlatform}`];
47
+ const platformVars = scopedVars[`${UNIWIND_PLATFORM_VARIABLES}${platform}`];
48
+ if (commonPlatformVars) {
49
+ Object.defineProperties(vars, Object.getOwnPropertyDescriptors(commonPlatformVars));
50
+ }
44
51
  if (platformVars) {
45
52
  Object.defineProperties(vars, Object.getOwnPropertyDescriptors(platformVars));
46
53
  }
@@ -48,7 +55,7 @@ class UniwindStoreBuilder {
48
55
  this.stylesheet = stylesheet;
49
56
  this.vars = Object.fromEntries(themes.map((theme) => {
50
57
  const clonedVars = cloneWithAccessors(vars);
51
- const themeVars = scopedVars[`__uniwind-theme-${theme}`];
58
+ const themeVars = scopedVars[`${UNIWIND_THEME_VARIABLES}${theme}`];
52
59
  if (themeVars) {
53
60
  Object.defineProperties(clonedVars, Object.getOwnPropertyDescriptors(themeVars));
54
61
  }
@@ -209,6 +216,16 @@ class UniwindStoreBuilder {
209
216
  }
210
217
  return true;
211
218
  }
219
+ getCurrentPlatform() {
220
+ const platform = Platform.OS;
221
+ if (platform === "android") {
222
+ return Platform.isTV ? UniwindPlatform.AndroidTV : UniwindPlatform.Android;
223
+ }
224
+ if (platform === "ios") {
225
+ return Platform.isTV ? UniwindPlatform.AppleTV : UniwindPlatform.iOS;
226
+ }
227
+ return platform;
228
+ }
212
229
  }
213
230
  export const UniwindStore = new UniwindStoreBuilder();
214
231
  Dimensions.addEventListener("change", ({ window }) => {
@@ -57,6 +57,7 @@ export type UniwindRuntime = {
57
57
  linear: (...points: ControlPoint[]) => LinearEasing;
58
58
  lightDark: (light: string, dark: string) => string;
59
59
  parseColor: (type: string, color: string) => string;
60
+ platformSelect: (android: string, ios: string, other?: string) => string;
60
61
  };
61
62
  export type RNStyle = ViewStyle & TextStyle & ImageStyle & {
62
63
  accentColor?: string;
@@ -1,4 +1,4 @@
1
- const variants = ["ios", "android", "web", "native"];
1
+ const variants = ["ios", "android", "web", "native", "tv", "android-tv", "apple-tv"];
2
2
  export const generateCSSForVariants = () => {
3
3
  let css = "";
4
4
  variants.forEach((variant) => {
@@ -1,6 +1,7 @@
1
1
  import { Function as LightningCSSFunction, TokenOrValue } from 'lightningcss';
2
2
  export declare class FunctionVisitor {
3
3
  [name: string]: (fn: LightningCSSFunction) => TokenOrValue;
4
+ platformSelect(fn: LightningCSSFunction): TokenOrValue;
4
5
  pixelRatio(fn: LightningCSSFunction): TokenOrValue;
5
6
  fontScale(fn: LightningCSSFunction): TokenOrValue;
6
7
  hairlineWidth(): TokenOrValue;
@@ -3,6 +3,9 @@ const ONE_PX = {
3
3
  value: { type: "dimension", unit: "px", value: 1 }
4
4
  };
5
5
  export class FunctionVisitor {
6
+ platformSelect(fn) {
7
+ return fn.arguments.at(0) ?? { type: "token", value: { type: "ident", value: "initial" } };
8
+ }
6
9
  pixelRatio(fn) {
7
10
  return {
8
11
  type: "function",
@@ -16,9 +16,11 @@ export declare class RuleVisitor implements LightningRuleVisitors {
16
16
  }>) => void;
17
17
  style: (styleRule: Extract<LightningRuleVisitor, {
18
18
  type: "style";
19
- }>) => void | ReturnedRule;
19
+ }>) => void | ReturnedRule | ReturnedRule[];
20
20
  cleanup(): void;
21
+ private processThemeRoot;
21
22
  private processThemeStyle;
22
23
  private processClassStyle;
24
+ private removeNulls;
23
25
  }
24
26
  export {};
@@ -10,9 +10,8 @@ export class RuleVisitor {
10
10
  };
11
11
  style = (styleRule) => {
12
12
  const firstSelector = styleRule.value.selectors.at(0)?.at(0);
13
- const secondSelector = styleRule.value.selectors.at(0)?.at(1);
14
- if (this.currentLayerName === "theme" && firstSelector?.type === "nesting" && secondSelector?.type === "pseudo-class" && secondSelector.kind === "where") {
15
- return this.processThemeStyle(styleRule, secondSelector);
13
+ if (this.currentLayerName === "theme" && firstSelector?.type === "pseudo-class" && firstSelector.kind === "root") {
14
+ return this.removeNulls(this.processThemeRoot(styleRule));
16
15
  }
17
16
  if (firstSelector?.type === "class") {
18
17
  return this.processClassStyle(styleRule, firstSelector);
@@ -23,14 +22,47 @@ export class RuleVisitor {
23
22
  this.processedClassNames.clear();
24
23
  this.processedVariables.clear();
25
24
  }
25
+ processThemeRoot(styleRule) {
26
+ const themeScopedRules = styleRule.value.rules?.filter((rule) => {
27
+ if (rule.type !== "style") {
28
+ return false;
29
+ }
30
+ const firstSelector = rule.value.selectors.at(0)?.at(0);
31
+ const secondSelector = rule.value.selectors.at(0)?.at(1);
32
+ return firstSelector?.type === "nesting" && secondSelector?.type === "pseudo-class" && secondSelector.kind === "where";
33
+ }) ?? [];
34
+ const nonThemeRules = styleRule.value.rules?.filter((rule) => !themeScopedRules.includes(rule));
35
+ const processedThemeScopedRules = themeScopedRules.map((rule) => {
36
+ if (rule.type !== "style") {
37
+ return rule;
38
+ }
39
+ const secondSelector = rule.value.selectors.at(0)?.at(1);
40
+ if (secondSelector?.type === "pseudo-class" && secondSelector.kind === "where") {
41
+ return this.processThemeStyle(rule, secondSelector);
42
+ }
43
+ return rule;
44
+ });
45
+ return [
46
+ {
47
+ type: "style",
48
+ value: {
49
+ loc: styleRule.value.loc,
50
+ selectors: styleRule.value.selectors,
51
+ rules: nonThemeRules,
52
+ declarations: styleRule.value.declarations
53
+ }
54
+ },
55
+ ...processedThemeScopedRules
56
+ ];
57
+ }
26
58
  processThemeStyle(styleRule, secondSelector) {
27
59
  const whereSelector = secondSelector.selectors.at(0)?.at(0);
28
60
  if (whereSelector?.type !== "class") {
29
- return;
61
+ return styleRule;
30
62
  }
31
63
  const selectedVariant = this.themes.find((theme) => whereSelector.name === theme);
32
64
  if (selectedVariant === void 0 || this.processedVariables.has(selectedVariant)) {
33
- return;
65
+ return styleRule;
34
66
  }
35
67
  this.processedVariables.add(selectedVariant);
36
68
  return {
@@ -59,4 +91,21 @@ export class RuleVisitor {
59
91
  }
60
92
  };
61
93
  }
94
+ // Fixes lightningcss serialization bug
95
+ removeNulls(value) {
96
+ if (Array.isArray(value)) {
97
+ return value.map((v) => this.removeNulls(v));
98
+ }
99
+ if (typeof value === "object" && value !== null) {
100
+ return Object.fromEntries(
101
+ Object.entries(value).filter(([_, value2]) => {
102
+ if (value2 === null) {
103
+ return false;
104
+ }
105
+ return true;
106
+ }).map(([key, value2]) => [key, this.removeNulls(value2)])
107
+ );
108
+ }
109
+ return value;
110
+ }
62
111
  }
@@ -0,0 +1,18 @@
1
+ 'use strict';
2
+
3
+ var Platform = /* @__PURE__ */ ((Platform2) => {
4
+ Platform2["Android"] = "android";
5
+ Platform2["iOS"] = "ios";
6
+ Platform2["Web"] = "web";
7
+ Platform2["Native"] = "native";
8
+ Platform2["TV"] = "tv";
9
+ Platform2["AndroidTV"] = "android-tv";
10
+ Platform2["AppleTV"] = "apple-tv";
11
+ return Platform2;
12
+ })(Platform || {});
13
+ const UNIWIND_PLATFORM_VARIABLES = "__uniwind-platform-";
14
+ const UNIWIND_THEME_VARIABLES = "__uniwind-theme-";
15
+
16
+ exports.Platform = Platform;
17
+ exports.UNIWIND_PLATFORM_VARIABLES = UNIWIND_PLATFORM_VARIABLES;
18
+ exports.UNIWIND_THEME_VARIABLES = UNIWIND_THEME_VARIABLES;
@@ -44,6 +44,9 @@ const ONE_PX = {
44
44
  value: { type: "dimension", unit: "px", value: 1 }
45
45
  };
46
46
  class FunctionVisitor {
47
+ platformSelect(fn) {
48
+ return fn.arguments.at(0) ?? { type: "token", value: { type: "ident", value: "initial" } };
49
+ }
47
50
  pixelRatio(fn) {
48
51
  return {
49
52
  type: "function",
@@ -90,9 +93,8 @@ class RuleVisitor {
90
93
  };
91
94
  style = (styleRule) => {
92
95
  const firstSelector = styleRule.value.selectors.at(0)?.at(0);
93
- const secondSelector = styleRule.value.selectors.at(0)?.at(1);
94
- if (this.currentLayerName === "theme" && firstSelector?.type === "nesting" && secondSelector?.type === "pseudo-class" && secondSelector.kind === "where") {
95
- return this.processThemeStyle(styleRule, secondSelector);
96
+ if (this.currentLayerName === "theme" && firstSelector?.type === "pseudo-class" && firstSelector.kind === "root") {
97
+ return this.removeNulls(this.processThemeRoot(styleRule));
96
98
  }
97
99
  if (firstSelector?.type === "class") {
98
100
  return this.processClassStyle(styleRule, firstSelector);
@@ -103,14 +105,47 @@ class RuleVisitor {
103
105
  this.processedClassNames.clear();
104
106
  this.processedVariables.clear();
105
107
  }
108
+ processThemeRoot(styleRule) {
109
+ const themeScopedRules = styleRule.value.rules?.filter((rule) => {
110
+ if (rule.type !== "style") {
111
+ return false;
112
+ }
113
+ const firstSelector = rule.value.selectors.at(0)?.at(0);
114
+ const secondSelector = rule.value.selectors.at(0)?.at(1);
115
+ return firstSelector?.type === "nesting" && secondSelector?.type === "pseudo-class" && secondSelector.kind === "where";
116
+ }) ?? [];
117
+ const nonThemeRules = styleRule.value.rules?.filter((rule) => !themeScopedRules.includes(rule));
118
+ const processedThemeScopedRules = themeScopedRules.map((rule) => {
119
+ if (rule.type !== "style") {
120
+ return rule;
121
+ }
122
+ const secondSelector = rule.value.selectors.at(0)?.at(1);
123
+ if (secondSelector?.type === "pseudo-class" && secondSelector.kind === "where") {
124
+ return this.processThemeStyle(rule, secondSelector);
125
+ }
126
+ return rule;
127
+ });
128
+ return [
129
+ {
130
+ type: "style",
131
+ value: {
132
+ loc: styleRule.value.loc,
133
+ selectors: styleRule.value.selectors,
134
+ rules: nonThemeRules,
135
+ declarations: styleRule.value.declarations
136
+ }
137
+ },
138
+ ...processedThemeScopedRules
139
+ ];
140
+ }
106
141
  processThemeStyle(styleRule, secondSelector) {
107
142
  const whereSelector = secondSelector.selectors.at(0)?.at(0);
108
143
  if (whereSelector?.type !== "class") {
109
- return;
144
+ return styleRule;
110
145
  }
111
146
  const selectedVariant = this.themes.find((theme) => whereSelector.name === theme);
112
147
  if (selectedVariant === void 0 || this.processedVariables.has(selectedVariant)) {
113
- return;
148
+ return styleRule;
114
149
  }
115
150
  this.processedVariables.add(selectedVariant);
116
151
  return {
@@ -139,6 +174,23 @@ class RuleVisitor {
139
174
  }
140
175
  };
141
176
  }
177
+ // Fixes lightningcss serialization bug
178
+ removeNulls(value) {
179
+ if (Array.isArray(value)) {
180
+ return value.map((v) => this.removeNulls(v));
181
+ }
182
+ if (typeof value === "object" && value !== null) {
183
+ return Object.fromEntries(
184
+ Object.entries(value).filter(([_, value2]) => {
185
+ if (value2 === null) {
186
+ return false;
187
+ }
188
+ return true;
189
+ }).map(([key, value2]) => [key, this.removeNulls(value2)])
190
+ );
191
+ }
192
+ return value;
193
+ }
142
194
  }
143
195
 
144
196
  class UniwindCSSVisitor {
@@ -372,7 +424,7 @@ const generateCSSForThemes = async (themes, input) => {
372
424
  return uniwindCSS;
373
425
  };
374
426
 
375
- const variants = ["ios", "android", "web", "native"];
427
+ const variants = ["ios", "android", "web", "native", "tv", "android-tv", "apple-tv"];
376
428
  const generateCSSForVariants = () => {
377
429
  let css = "";
378
430
  variants.forEach((variant) => {
@@ -51,6 +51,9 @@ const ONE_PX = {
51
51
  value: { type: "dimension", unit: "px", value: 1 }
52
52
  };
53
53
  class FunctionVisitor {
54
+ platformSelect(fn) {
55
+ return fn.arguments.at(0) ?? { type: "token", value: { type: "ident", value: "initial" } };
56
+ }
54
57
  pixelRatio(fn) {
55
58
  return {
56
59
  type: "function",
@@ -97,9 +100,8 @@ class RuleVisitor {
97
100
  };
98
101
  style = (styleRule) => {
99
102
  const firstSelector = styleRule.value.selectors.at(0)?.at(0);
100
- const secondSelector = styleRule.value.selectors.at(0)?.at(1);
101
- if (this.currentLayerName === "theme" && firstSelector?.type === "nesting" && secondSelector?.type === "pseudo-class" && secondSelector.kind === "where") {
102
- return this.processThemeStyle(styleRule, secondSelector);
103
+ if (this.currentLayerName === "theme" && firstSelector?.type === "pseudo-class" && firstSelector.kind === "root") {
104
+ return this.removeNulls(this.processThemeRoot(styleRule));
103
105
  }
104
106
  if (firstSelector?.type === "class") {
105
107
  return this.processClassStyle(styleRule, firstSelector);
@@ -110,14 +112,47 @@ class RuleVisitor {
110
112
  this.processedClassNames.clear();
111
113
  this.processedVariables.clear();
112
114
  }
115
+ processThemeRoot(styleRule) {
116
+ const themeScopedRules = styleRule.value.rules?.filter((rule) => {
117
+ if (rule.type !== "style") {
118
+ return false;
119
+ }
120
+ const firstSelector = rule.value.selectors.at(0)?.at(0);
121
+ const secondSelector = rule.value.selectors.at(0)?.at(1);
122
+ return firstSelector?.type === "nesting" && secondSelector?.type === "pseudo-class" && secondSelector.kind === "where";
123
+ }) ?? [];
124
+ const nonThemeRules = styleRule.value.rules?.filter((rule) => !themeScopedRules.includes(rule));
125
+ const processedThemeScopedRules = themeScopedRules.map((rule) => {
126
+ if (rule.type !== "style") {
127
+ return rule;
128
+ }
129
+ const secondSelector = rule.value.selectors.at(0)?.at(1);
130
+ if (secondSelector?.type === "pseudo-class" && secondSelector.kind === "where") {
131
+ return this.processThemeStyle(rule, secondSelector);
132
+ }
133
+ return rule;
134
+ });
135
+ return [
136
+ {
137
+ type: "style",
138
+ value: {
139
+ loc: styleRule.value.loc,
140
+ selectors: styleRule.value.selectors,
141
+ rules: nonThemeRules,
142
+ declarations: styleRule.value.declarations
143
+ }
144
+ },
145
+ ...processedThemeScopedRules
146
+ ];
147
+ }
113
148
  processThemeStyle(styleRule, secondSelector) {
114
149
  const whereSelector = secondSelector.selectors.at(0)?.at(0);
115
150
  if (whereSelector?.type !== "class") {
116
- return;
151
+ return styleRule;
117
152
  }
118
153
  const selectedVariant = this.themes.find((theme) => whereSelector.name === theme);
119
154
  if (selectedVariant === void 0 || this.processedVariables.has(selectedVariant)) {
120
- return;
155
+ return styleRule;
121
156
  }
122
157
  this.processedVariables.add(selectedVariant);
123
158
  return {
@@ -146,6 +181,23 @@ class RuleVisitor {
146
181
  }
147
182
  };
148
183
  }
184
+ // Fixes lightningcss serialization bug
185
+ removeNulls(value) {
186
+ if (Array.isArray(value)) {
187
+ return value.map((v) => this.removeNulls(v));
188
+ }
189
+ if (typeof value === "object" && value !== null) {
190
+ return Object.fromEntries(
191
+ Object.entries(value).filter(([_, value2]) => {
192
+ if (value2 === null) {
193
+ return false;
194
+ }
195
+ return true;
196
+ }).map(([key, value2]) => [key, this.removeNulls(value2)])
197
+ );
198
+ }
199
+ return value;
200
+ }
149
201
  }
150
202
 
151
203
  class UniwindCSSVisitor {
@@ -379,7 +431,7 @@ const generateCSSForThemes = async (themes, input) => {
379
431
  return uniwindCSS;
380
432
  };
381
433
 
382
- const variants = ["ios", "android", "web", "native"];
434
+ const variants = ["ios", "android", "web", "native", "tv", "android-tv", "apple-tv"];
383
435
  const generateCSSForVariants = () => {
384
436
  let css = "";
385
437
  variants.forEach((variant) => {
@@ -0,0 +1,14 @@
1
+ var Platform = /* @__PURE__ */ ((Platform2) => {
2
+ Platform2["Android"] = "android";
3
+ Platform2["iOS"] = "ios";
4
+ Platform2["Web"] = "web";
5
+ Platform2["Native"] = "native";
6
+ Platform2["TV"] = "tv";
7
+ Platform2["AndroidTV"] = "android-tv";
8
+ Platform2["AppleTV"] = "apple-tv";
9
+ return Platform2;
10
+ })(Platform || {});
11
+ const UNIWIND_PLATFORM_VARIABLES = "__uniwind-platform-";
12
+ const UNIWIND_THEME_VARIABLES = "__uniwind-theme-";
13
+
14
+ export { Platform as P, UNIWIND_PLATFORM_VARIABLES as U, UNIWIND_THEME_VARIABLES as a };
@@ -3,7 +3,7 @@
3
3
  const node = require('@tailwindcss/node');
4
4
  const path = require('path');
5
5
  const common = require('../shared/uniwind.nl8746mK.cjs');
6
- const stringifyThemes = require('../shared/uniwind.CyACT0sD.cjs');
6
+ const stringifyThemes = require('../shared/uniwind.DTMo4epG.cjs');
7
7
  require('fs');
8
8
  require('lightningcss');
9
9
 
@@ -1,7 +1,7 @@
1
1
  import { normalizePath } from '@tailwindcss/node';
2
2
  import path from 'path';
3
3
  import { u as uniq, n as name } from '../shared/uniwind.F-0-Rr--.mjs';
4
- import { s as stringifyThemes, U as UniwindCSSVisitor, a as buildCSS, b as buildDtsFile } from '../shared/uniwind.D-ahjOrG.mjs';
4
+ import { s as stringifyThemes, U as UniwindCSSVisitor, a as buildCSS, b as buildDtsFile } from '../shared/uniwind.BWb5KNML.mjs';
5
5
  import 'fs';
6
6
  import 'lightningcss';
7
7
 
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "@niibase/uniwind",
4
- "version": "1.4.0",
5
- "description": "The fastest Tailwind bindings for React Native with Reanimated 4 support",
4
+ "version": "1.5.0",
5
+ "description": "A fork of Uniwind with Reanimated 4 support",
6
6
  "homepage": "https://uniwind.dev",
7
7
  "author": "Unistack",
8
8
  "type": "module",
9
- "repository": "https://github.com/divineniiquaye/uniwind",
9
+ "repository": "https://github.com/divineniiquaye/uniwind-oss",
10
10
  "sideEffects": false,
11
11
  "scripts": {
12
12
  "precommit": "bun lint",
@@ -18,13 +18,15 @@
18
18
  "prepublishOnly": "bun run build",
19
19
  "circular:check": "dpdm --no-warning --no-tree -T --exit-code circular:1 'src/**/*.ts' 'src/**/*.tsx'",
20
20
  "build:css": "bun run src/css/index.ts",
21
- "postinstall": "node scripts/postinstall.mjs",
22
21
  "test:native": "jest --config jest.config.native.js",
23
- "test:web": "jest --config jest.config.web.js"
22
+ "test:web": "jest --config jest.config.web.js",
23
+ "test:e2e": "playwright test",
24
+ "release": "release-it"
24
25
  },
25
26
  "keywords": [
26
27
  "unistyles",
27
28
  "react-native-unistyles",
29
+ "uniwind",
28
30
  "react-native",
29
31
  "react",
30
32
  "native",
@@ -78,8 +80,8 @@
78
80
  "LICENSE"
79
81
  ],
80
82
  "dependencies": {
81
- "@tailwindcss/node": "4.1.17",
82
- "@tailwindcss/oxide": "4.1.17",
83
+ "@tailwindcss/node": "4.2.1",
84
+ "@tailwindcss/oxide": "4.2.1",
83
85
  "culori": "4.0.2",
84
86
  "lightningcss": "1.30.1"
85
87
  },
@@ -92,7 +94,8 @@
92
94
  "tailwindcss": ">=4"
93
95
  },
94
96
  "devDependencies": {
95
- "@react-native/babel-preset": "0.83.0",
97
+ "@playwright/test": "1.58.2",
98
+ "@react-native/babel-preset": "0.84.1",
96
99
  "@testing-library/jest-dom": "6.9.1",
97
100
  "@testing-library/jest-native": "5.4.3",
98
101
  "@testing-library/react": "16.3.2",
@@ -101,19 +104,22 @@
101
104
  "@types/culori": "4.0.1",
102
105
  "@types/jest": "30.0.0",
103
106
  "@types/react": "catalog:",
104
- "metro": "0.83.3",
105
- "dpdm": "3.14.0",
107
+ "dpdm": "4.0.1",
108
+ "git-cliff": "2.12.0",
106
109
  "jest": "30.2.0",
107
110
  "jest-environment-jsdom": "30.2.0",
108
- "prettier": "3.7.4",
111
+ "metro": "0.84.2",
112
+ "prettier": "3.8.1",
109
113
  "react-native-gesture-handler": "2.28.0",
110
114
  "react-native-reanimated": "catalog:",
111
115
  "react-native-safe-area-context": "5.6.0",
112
116
  "react-native-web": "catalog:",
113
- "react-test-renderer": "19.1.0",
117
+ "react-test-renderer": "19.2.4",
118
+ "release-it": "19.2.4",
114
119
  "ts-jest": "29.4.6",
115
120
  "typescript": "catalog:",
116
121
  "unbuild": "3.6.1",
117
- "vite": "7.2.7"
122
+ "vite": "7.3.1",
123
+ "esbuild": "0.27.3"
118
124
  }
119
125
  }
package/readme.md CHANGED
@@ -15,7 +15,7 @@
15
15
  [![npm downloads](https://img.shields.io/npm/dt/uniwind?style=for-the-badge)](https://www.npmjs.com/package/uniwind)
16
16
  [![License: MIT](https://img.shields.io/badge/License-MIT-44CD11.svg?style=for-the-badge)](https://opensource.org/licenses/MIT)
17
17
 
18
- > **Note:** This is a fork of uniwind that makes pro subscription features available for free (except for Unistyles support). Install from `@niibase/uniwind` to get these features.
18
+ > **Note:** This is a fork of **uniwind** that leverages **react-native-reanimated** (v4.0.0+) to enable CSS-like animations and transitions using Tailwind classes. This package is kept in sync with the original `uniwind` releases.
19
19
 
20
20
  ## Installation
21
21
 
@@ -24,7 +24,7 @@
24
24
  bun add @niibase/uniwind tailwindcss react-native-safe-area-context react-native-reanimated
25
25
 
26
26
  # Or install from the original package
27
- bun add uniwind tailwindcss react-native-safe-area-context react-native-reanimated
27
+ bun add uniwind tailwindcss react-native-safe-area-context
28
28
 
29
29
  # Or install @niibase/uniwind as alias for uniwind
30
30
  bun add uniwind@npm:@niibase/uniwind tailwindcss react-native-safe-area-context react-native-reanimated
@@ -41,8 +41,8 @@ Then follow [installation guides](https://docs.uniwind.dev/quickstart)
41
41
 
42
42
  ## Features
43
43
 
44
- - ⚛️ Out‑of‑the‑box `className` bindings for every React Native component
45
- - ⚡ Styles are computed at build time for maximum performance
44
+ - ⚛️ Out‑of‑the‑box `className` bindings for every React Native component
45
+ - ⚡ Styles are computed at build time for maximum performance
46
46
  - 🎬 **CSS animations and transitions** powered by Reanimated 4
47
47
  - 🌙 Dark mode and 🎨 fully customizable themes
48
48
  - 🧩 Pseudo‑classes support — `focus`, `active`, `disabled`, and more
@@ -50,14 +50,6 @@ Then follow [installation guides](https://docs.uniwind.dev/quickstart)
50
50
  - 🧰 Use custom CSS properties directly in React Native
51
51
  - 🔥 And [much more](https://docs.uniwind.dev/api/use-uniwind)
52
52
 
53
- ## Roadmap
54
-
55
- - [x] **Reanimated 4 support** - Full compatibility with React Native Reanimated v4 ✅
56
- - [ ] **Babel support to extend beyond React Native components** - Enable className support for custom components and third-party libraries
57
- - [ ] **Unistyles (future)** - Integration with Unistyles for enhanced styling capabilities
58
-
59
- > **Note:** Once Babel support is completed, the likelihood of adding Unistyles support is high.
60
-
61
53
  ## Contributing
62
54
 
63
55
  Contributions are welcome!