@scripso-homepad/ui 0.3.1 → 0.3.2

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/dist/index.cjs CHANGED
@@ -5,6 +5,38 @@ var reactNative = require('react-native');
5
5
  var jsxRuntime = require('react/jsx-runtime');
6
6
 
7
7
  // src/components/Button.tsx
8
+ function rnStyleToWebStyle(style) {
9
+ const flat = reactNative.StyleSheet.flatten(style);
10
+ if (!flat) return {};
11
+ const css = {};
12
+ for (const [key, value] of Object.entries(flat)) {
13
+ if (value === void 0 || value === null) continue;
14
+ switch (key) {
15
+ case "paddingVertical":
16
+ css.paddingTop = value;
17
+ css.paddingBottom = value;
18
+ break;
19
+ case "paddingHorizontal":
20
+ css.paddingLeft = value;
21
+ css.paddingRight = value;
22
+ break;
23
+ case "marginVertical":
24
+ css.marginTop = value;
25
+ css.marginBottom = value;
26
+ break;
27
+ case "marginHorizontal":
28
+ css.marginLeft = value;
29
+ css.marginRight = value;
30
+ break;
31
+ default:
32
+ css[key] = value;
33
+ }
34
+ }
35
+ if (css.alignItems || css.justifyContent || css.flexDirection) {
36
+ css.display = css.display ?? "flex";
37
+ }
38
+ return css;
39
+ }
8
40
  function Button({
9
41
  title,
10
42
  onPress,
@@ -16,13 +48,14 @@ function Button({
16
48
  }) {
17
49
  const containerStyle = [styles.button, disabled && styles.buttonDisabled, style];
18
50
  const labelStyle = [styles.text, disabled && styles.textDisabled, textStyle];
19
- if (reactNative.Platform.OS === "web") {
51
+ const useWebDom = reactNative.Platform.OS === "web" && (className != null || textClassName != null);
52
+ if (useWebDom) {
20
53
  return react.createElement(
21
54
  "button",
22
55
  {
23
56
  type: "button",
24
57
  className,
25
- style: reactNative.StyleSheet.flatten(containerStyle),
58
+ style: rnStyleToWebStyle(containerStyle),
26
59
  disabled,
27
60
  onClick: (event) => {
28
61
  onPress(event);
@@ -33,7 +66,7 @@ function Button({
33
66
  "span",
34
67
  {
35
68
  className: textClassName,
36
- style: reactNative.StyleSheet.flatten(labelStyle)
69
+ style: rnStyleToWebStyle(labelStyle)
37
70
  },
38
71
  title
39
72
  )
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/Button.tsx"],"names":["Platform","createElement","StyleSheet","jsx","TouchableOpacity","Text"],"mappings":";;;;;;;AAkCO,SAAS,MAAA,CAAO;AAAA,EACrB,KAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA,GAAW,KAAA;AAAA,EACX,KAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA;AACF,CAAA,EAAgB;AACd,EAAA,MAAM,iBAAiB,CAAC,MAAA,CAAO,QAAQ,QAAA,IAAY,MAAA,CAAO,gBAAgB,KAAK,CAAA;AAC/E,EAAA,MAAM,aAAa,CAAC,MAAA,CAAO,MAAM,QAAA,IAAY,MAAA,CAAO,cAAc,SAAS,CAAA;AAE3E,EAAA,IAAIA,oBAAA,CAAS,OAAO,KAAA,EAAO;AACzB,IAAA,OAAOC,mBAAA;AAAA,MACL,QAAA;AAAA,MACA;AAAA,QACE,IAAA,EAAM,QAAA;AAAA,QACN,SAAA;AAAA,QACA,KAAA,EAAOC,sBAAA,CAAW,OAAA,CAAQ,cAAc,CAAA;AAAA,QACxC,QAAA;AAAA,QACA,OAAA,EAAS,CAAC,KAAA,KAA+C;AACvD,UAAA,OAAA,CAAQ,KAAyC,CAAA;AAAA,QACnD,CAAA;AAAA,QACA,eAAA,EAAiB;AAAA,OACnB;AAAA,MACAD,mBAAA;AAAA,QACE,MAAA;AAAA,QACA;AAAA,UACE,SAAA,EAAW,aAAA;AAAA,UACX,KAAA,EAAOC,sBAAA,CAAW,OAAA,CAAQ,UAAU;AAAA,SACtC;AAAA,QACA;AAAA;AACF,KACF;AAAA,EACF;AAEA,EAAA,uBACEC,cAAA;AAAA,IAACC,4BAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,cAAA;AAAA,MACP,OAAA;AAAA,MACA,QAAA;AAAA,MACA,aAAA,EAAe,GAAA;AAAA,MACf,iBAAA,EAAkB,QAAA;AAAA,MAClB,kBAAA,EAAoB,EAAE,QAAA,EAAS;AAAA,MAE/B,QAAA,kBAAAD,cAAA,CAACE,gBAAA,EAAA,EAAK,KAAA,EAAO,UAAA,EAAa,QAAA,EAAA,KAAA,EAAM;AAAA;AAAA,GAClC;AAEJ;AAEA,IAAM,MAAA,GAASH,uBAAW,MAAA,CAAO;AAAA,EAC/B,MAAA,EAAQ;AAAA,IACN,eAAA,EAAiB,SAAA;AAAA,IACjB,eAAA,EAAiB,EAAA;AAAA,IACjB,iBAAA,EAAmB,EAAA;AAAA,IACnB,YAAA,EAAc,CAAA;AAAA,IACd,UAAA,EAAY,QAAA;AAAA,IACZ,cAAA,EAAgB,QAAA;AAAA,IAChB,QAAA,EAAU,GAAA;AAAA,IACV,WAAA,EAAa;AAAA,GACf;AAAA,EACA,cAAA,EAAgB;AAAA,IACd,eAAA,EAAiB,SAAA;AAAA,IACjB,OAAA,EAAS;AAAA,GACX;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,KAAA,EAAO,SAAA;AAAA,IACP,QAAA,EAAU,EAAA;AAAA,IACV,UAAA,EAAY;AAAA,GACd;AAAA,EACA,YAAA,EAAc;AAAA,IACZ,KAAA,EAAO;AAAA;AAEX,CAAC,CAAA","file":"index.cjs","sourcesContent":["import React, { createElement } from \"react\";\nimport {\n Platform,\n StyleSheet,\n Text,\n TouchableOpacity,\n type GestureResponderEvent,\n type StyleProp,\n type TextStyle,\n type ViewStyle,\n} from \"react-native\";\n\nexport interface ButtonProps {\n title: string;\n onPress: (event: GestureResponderEvent) => void;\n disabled?: boolean;\n /** Additional container styles (works on web and native). */\n style?: StyleProp<ViewStyle>;\n /** Additional label styles (works on web and native). */\n textStyle?: StyleProp<TextStyle>;\n /**\n * CSS class names for the container.\n * On web: applied to the underlying `<button>` element (Tailwind works).\n * On native: ignored unless using NativeWind with cssInterop.\n */\n className?: string;\n /**\n * CSS class names for the label.\n * On web: applied to the underlying `<span>` element (Tailwind works).\n * On native: ignored unless using NativeWind with cssInterop.\n */\n textClassName?: string;\n}\n\nexport function Button({\n title,\n onPress,\n disabled = false,\n style,\n textStyle,\n className,\n textClassName,\n}: ButtonProps) {\n const containerStyle = [styles.button, disabled && styles.buttonDisabled, style];\n const labelStyle = [styles.text, disabled && styles.textDisabled, textStyle];\n\n if (Platform.OS === \"web\") {\n return createElement(\n \"button\",\n {\n type: \"button\",\n className,\n style: StyleSheet.flatten(containerStyle),\n disabled,\n onClick: (event: React.MouseEvent<HTMLButtonElement>) => {\n onPress(event as unknown as GestureResponderEvent);\n },\n \"aria-disabled\": disabled,\n },\n createElement(\n \"span\",\n {\n className: textClassName,\n style: StyleSheet.flatten(labelStyle),\n },\n title,\n ),\n );\n }\n\n return (\n <TouchableOpacity\n style={containerStyle}\n onPress={onPress}\n disabled={disabled}\n activeOpacity={0.7}\n accessibilityRole=\"button\"\n accessibilityState={{ disabled }}\n >\n <Text style={labelStyle}>{title}</Text>\n </TouchableOpacity>\n );\n}\n\nconst styles = StyleSheet.create({\n button: {\n backgroundColor: \"#2563eb\",\n paddingVertical: 12,\n paddingHorizontal: 24,\n borderRadius: 8,\n alignItems: \"center\",\n justifyContent: \"center\",\n minWidth: 120,\n borderWidth: 0,\n },\n buttonDisabled: {\n backgroundColor: \"#93c5fd\",\n opacity: 0.7,\n },\n text: {\n color: \"#ffffff\",\n fontSize: 16,\n fontWeight: \"600\",\n },\n textDisabled: {\n color: \"#e5e7eb\",\n },\n});\n"]}
1
+ {"version":3,"sources":["../src/utils/rnStyleToWebStyle.ts","../src/components/Button.tsx"],"names":["StyleSheet","Platform","createElement","jsx","TouchableOpacity","Text"],"mappings":"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","file":"index.cjs","sourcesContent":["import { StyleSheet, type StyleProp, type TextStyle, type ViewStyle } from \"react-native\";\nimport type { CSSProperties } from \"react\";\n\ntype RNStyle = StyleProp<ViewStyle> | StyleProp<TextStyle>;\n\n/**\n * Converts React Native StyleSheet values to CSS properties for DOM elements.\n * Required when rendering native HTML on web (browsers ignore paddingVertical, etc.).\n */\nexport function rnStyleToWebStyle(style: RNStyle): CSSProperties {\n const flat = StyleSheet.flatten(style);\n if (!flat) return {};\n\n const css: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(flat)) {\n if (value === undefined || value === null) continue;\n\n switch (key) {\n case \"paddingVertical\":\n css.paddingTop = value;\n css.paddingBottom = value;\n break;\n case \"paddingHorizontal\":\n css.paddingLeft = value;\n css.paddingRight = value;\n break;\n case \"marginVertical\":\n css.marginTop = value;\n css.marginBottom = value;\n break;\n case \"marginHorizontal\":\n css.marginLeft = value;\n css.marginRight = value;\n break;\n default:\n css[key] = value;\n }\n }\n\n if (css.alignItems || css.justifyContent || css.flexDirection) {\n css.display = css.display ?? \"flex\";\n }\n\n return css as CSSProperties;\n}\n","import React, { createElement } from \"react\";\nimport {\n Platform,\n StyleSheet,\n Text,\n TouchableOpacity,\n type GestureResponderEvent,\n type StyleProp,\n type TextStyle,\n type ViewStyle,\n} from \"react-native\";\nimport { rnStyleToWebStyle } from \"../utils/rnStyleToWebStyle\";\n\nexport interface ButtonProps {\n title: string;\n onPress: (event: GestureResponderEvent) => void;\n disabled?: boolean;\n /** Additional container styles (works on web and native). */\n style?: StyleProp<ViewStyle>;\n /** Additional label styles (works on web and native). */\n textStyle?: StyleProp<TextStyle>;\n /**\n * CSS class names for the container (web only uses DOM `<button>` for Tailwind).\n * On native: ignored unless using NativeWind with cssInterop.\n */\n className?: string;\n /**\n * CSS class names for the label (web only).\n * On native: ignored unless using NativeWind with cssInterop.\n */\n textClassName?: string;\n}\n\nexport function Button({\n title,\n onPress,\n disabled = false,\n style,\n textStyle,\n className,\n textClassName,\n}: ButtonProps) {\n const containerStyle = [styles.button, disabled && styles.buttonDisabled, style];\n const labelStyle = [styles.text, disabled && styles.textDisabled, textStyle];\n\n const useWebDom =\n Platform.OS === \"web\" && (className != null || textClassName != null);\n\n if (useWebDom) {\n return createElement(\n \"button\",\n {\n type: \"button\",\n className,\n style: rnStyleToWebStyle(containerStyle),\n disabled,\n onClick: (event: React.MouseEvent<HTMLButtonElement>) => {\n onPress(event as unknown as GestureResponderEvent);\n },\n \"aria-disabled\": disabled,\n },\n createElement(\n \"span\",\n {\n className: textClassName,\n style: rnStyleToWebStyle(labelStyle),\n },\n title,\n ),\n );\n }\n\n return (\n <TouchableOpacity\n style={containerStyle}\n onPress={onPress}\n disabled={disabled}\n activeOpacity={0.7}\n accessibilityRole=\"button\"\n accessibilityState={{ disabled }}\n >\n <Text style={labelStyle}>{title}</Text>\n </TouchableOpacity>\n );\n}\n\nconst styles = StyleSheet.create({\n button: {\n backgroundColor: \"#2563eb\",\n paddingVertical: 12,\n paddingHorizontal: 24,\n borderRadius: 8,\n alignItems: \"center\",\n justifyContent: \"center\",\n minWidth: 120,\n borderWidth: 0,\n },\n buttonDisabled: {\n backgroundColor: \"#93c5fd\",\n opacity: 0.7,\n },\n text: {\n color: \"#ffffff\",\n fontSize: 16,\n fontWeight: \"600\",\n },\n textDisabled: {\n color: \"#e5e7eb\",\n },\n});\n"]}
package/dist/index.d.cts CHANGED
@@ -10,14 +10,12 @@ interface ButtonProps {
10
10
  /** Additional label styles (works on web and native). */
11
11
  textStyle?: StyleProp<TextStyle>;
12
12
  /**
13
- * CSS class names for the container.
14
- * On web: applied to the underlying `<button>` element (Tailwind works).
13
+ * CSS class names for the container (web only — uses DOM `<button>` for Tailwind).
15
14
  * On native: ignored unless using NativeWind with cssInterop.
16
15
  */
17
16
  className?: string;
18
17
  /**
19
- * CSS class names for the label.
20
- * On web: applied to the underlying `<span>` element (Tailwind works).
18
+ * CSS class names for the label (web only).
21
19
  * On native: ignored unless using NativeWind with cssInterop.
22
20
  */
23
21
  textClassName?: string;
package/dist/index.d.ts CHANGED
@@ -10,14 +10,12 @@ interface ButtonProps {
10
10
  /** Additional label styles (works on web and native). */
11
11
  textStyle?: StyleProp<TextStyle>;
12
12
  /**
13
- * CSS class names for the container.
14
- * On web: applied to the underlying `<button>` element (Tailwind works).
13
+ * CSS class names for the container (web only — uses DOM `<button>` for Tailwind).
15
14
  * On native: ignored unless using NativeWind with cssInterop.
16
15
  */
17
16
  className?: string;
18
17
  /**
19
- * CSS class names for the label.
20
- * On web: applied to the underlying `<span>` element (Tailwind works).
18
+ * CSS class names for the label (web only).
21
19
  * On native: ignored unless using NativeWind with cssInterop.
22
20
  */
23
21
  textClassName?: string;
package/dist/index.js CHANGED
@@ -3,6 +3,38 @@ import { StyleSheet, Platform, TouchableOpacity, Text } from 'react-native';
3
3
  import { jsx } from 'react/jsx-runtime';
4
4
 
5
5
  // src/components/Button.tsx
6
+ function rnStyleToWebStyle(style) {
7
+ const flat = StyleSheet.flatten(style);
8
+ if (!flat) return {};
9
+ const css = {};
10
+ for (const [key, value] of Object.entries(flat)) {
11
+ if (value === void 0 || value === null) continue;
12
+ switch (key) {
13
+ case "paddingVertical":
14
+ css.paddingTop = value;
15
+ css.paddingBottom = value;
16
+ break;
17
+ case "paddingHorizontal":
18
+ css.paddingLeft = value;
19
+ css.paddingRight = value;
20
+ break;
21
+ case "marginVertical":
22
+ css.marginTop = value;
23
+ css.marginBottom = value;
24
+ break;
25
+ case "marginHorizontal":
26
+ css.marginLeft = value;
27
+ css.marginRight = value;
28
+ break;
29
+ default:
30
+ css[key] = value;
31
+ }
32
+ }
33
+ if (css.alignItems || css.justifyContent || css.flexDirection) {
34
+ css.display = css.display ?? "flex";
35
+ }
36
+ return css;
37
+ }
6
38
  function Button({
7
39
  title,
8
40
  onPress,
@@ -14,13 +46,14 @@ function Button({
14
46
  }) {
15
47
  const containerStyle = [styles.button, disabled && styles.buttonDisabled, style];
16
48
  const labelStyle = [styles.text, disabled && styles.textDisabled, textStyle];
17
- if (Platform.OS === "web") {
49
+ const useWebDom = Platform.OS === "web" && (className != null || textClassName != null);
50
+ if (useWebDom) {
18
51
  return createElement(
19
52
  "button",
20
53
  {
21
54
  type: "button",
22
55
  className,
23
- style: StyleSheet.flatten(containerStyle),
56
+ style: rnStyleToWebStyle(containerStyle),
24
57
  disabled,
25
58
  onClick: (event) => {
26
59
  onPress(event);
@@ -31,7 +64,7 @@ function Button({
31
64
  "span",
32
65
  {
33
66
  className: textClassName,
34
- style: StyleSheet.flatten(labelStyle)
67
+ style: rnStyleToWebStyle(labelStyle)
35
68
  },
36
69
  title
37
70
  )
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/Button.tsx"],"names":[],"mappings":";;;;;AAkCO,SAAS,MAAA,CAAO;AAAA,EACrB,KAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA,GAAW,KAAA;AAAA,EACX,KAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA;AACF,CAAA,EAAgB;AACd,EAAA,MAAM,iBAAiB,CAAC,MAAA,CAAO,QAAQ,QAAA,IAAY,MAAA,CAAO,gBAAgB,KAAK,CAAA;AAC/E,EAAA,MAAM,aAAa,CAAC,MAAA,CAAO,MAAM,QAAA,IAAY,MAAA,CAAO,cAAc,SAAS,CAAA;AAE3E,EAAA,IAAI,QAAA,CAAS,OAAO,KAAA,EAAO;AACzB,IAAA,OAAO,aAAA;AAAA,MACL,QAAA;AAAA,MACA;AAAA,QACE,IAAA,EAAM,QAAA;AAAA,QACN,SAAA;AAAA,QACA,KAAA,EAAO,UAAA,CAAW,OAAA,CAAQ,cAAc,CAAA;AAAA,QACxC,QAAA;AAAA,QACA,OAAA,EAAS,CAAC,KAAA,KAA+C;AACvD,UAAA,OAAA,CAAQ,KAAyC,CAAA;AAAA,QACnD,CAAA;AAAA,QACA,eAAA,EAAiB;AAAA,OACnB;AAAA,MACA,aAAA;AAAA,QACE,MAAA;AAAA,QACA;AAAA,UACE,SAAA,EAAW,aAAA;AAAA,UACX,KAAA,EAAO,UAAA,CAAW,OAAA,CAAQ,UAAU;AAAA,SACtC;AAAA,QACA;AAAA;AACF,KACF;AAAA,EACF;AAEA,EAAA,uBACE,GAAA;AAAA,IAAC,gBAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,cAAA;AAAA,MACP,OAAA;AAAA,MACA,QAAA;AAAA,MACA,aAAA,EAAe,GAAA;AAAA,MACf,iBAAA,EAAkB,QAAA;AAAA,MAClB,kBAAA,EAAoB,EAAE,QAAA,EAAS;AAAA,MAE/B,QAAA,kBAAA,GAAA,CAAC,IAAA,EAAA,EAAK,KAAA,EAAO,UAAA,EAAa,QAAA,EAAA,KAAA,EAAM;AAAA;AAAA,GAClC;AAEJ;AAEA,IAAM,MAAA,GAAS,WAAW,MAAA,CAAO;AAAA,EAC/B,MAAA,EAAQ;AAAA,IACN,eAAA,EAAiB,SAAA;AAAA,IACjB,eAAA,EAAiB,EAAA;AAAA,IACjB,iBAAA,EAAmB,EAAA;AAAA,IACnB,YAAA,EAAc,CAAA;AAAA,IACd,UAAA,EAAY,QAAA;AAAA,IACZ,cAAA,EAAgB,QAAA;AAAA,IAChB,QAAA,EAAU,GAAA;AAAA,IACV,WAAA,EAAa;AAAA,GACf;AAAA,EACA,cAAA,EAAgB;AAAA,IACd,eAAA,EAAiB,SAAA;AAAA,IACjB,OAAA,EAAS;AAAA,GACX;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,KAAA,EAAO,SAAA;AAAA,IACP,QAAA,EAAU,EAAA;AAAA,IACV,UAAA,EAAY;AAAA,GACd;AAAA,EACA,YAAA,EAAc;AAAA,IACZ,KAAA,EAAO;AAAA;AAEX,CAAC,CAAA","file":"index.js","sourcesContent":["import React, { createElement } from \"react\";\nimport {\n Platform,\n StyleSheet,\n Text,\n TouchableOpacity,\n type GestureResponderEvent,\n type StyleProp,\n type TextStyle,\n type ViewStyle,\n} from \"react-native\";\n\nexport interface ButtonProps {\n title: string;\n onPress: (event: GestureResponderEvent) => void;\n disabled?: boolean;\n /** Additional container styles (works on web and native). */\n style?: StyleProp<ViewStyle>;\n /** Additional label styles (works on web and native). */\n textStyle?: StyleProp<TextStyle>;\n /**\n * CSS class names for the container.\n * On web: applied to the underlying `<button>` element (Tailwind works).\n * On native: ignored unless using NativeWind with cssInterop.\n */\n className?: string;\n /**\n * CSS class names for the label.\n * On web: applied to the underlying `<span>` element (Tailwind works).\n * On native: ignored unless using NativeWind with cssInterop.\n */\n textClassName?: string;\n}\n\nexport function Button({\n title,\n onPress,\n disabled = false,\n style,\n textStyle,\n className,\n textClassName,\n}: ButtonProps) {\n const containerStyle = [styles.button, disabled && styles.buttonDisabled, style];\n const labelStyle = [styles.text, disabled && styles.textDisabled, textStyle];\n\n if (Platform.OS === \"web\") {\n return createElement(\n \"button\",\n {\n type: \"button\",\n className,\n style: StyleSheet.flatten(containerStyle),\n disabled,\n onClick: (event: React.MouseEvent<HTMLButtonElement>) => {\n onPress(event as unknown as GestureResponderEvent);\n },\n \"aria-disabled\": disabled,\n },\n createElement(\n \"span\",\n {\n className: textClassName,\n style: StyleSheet.flatten(labelStyle),\n },\n title,\n ),\n );\n }\n\n return (\n <TouchableOpacity\n style={containerStyle}\n onPress={onPress}\n disabled={disabled}\n activeOpacity={0.7}\n accessibilityRole=\"button\"\n accessibilityState={{ disabled }}\n >\n <Text style={labelStyle}>{title}</Text>\n </TouchableOpacity>\n );\n}\n\nconst styles = StyleSheet.create({\n button: {\n backgroundColor: \"#2563eb\",\n paddingVertical: 12,\n paddingHorizontal: 24,\n borderRadius: 8,\n alignItems: \"center\",\n justifyContent: \"center\",\n minWidth: 120,\n borderWidth: 0,\n },\n buttonDisabled: {\n backgroundColor: \"#93c5fd\",\n opacity: 0.7,\n },\n text: {\n color: \"#ffffff\",\n fontSize: 16,\n fontWeight: \"600\",\n },\n textDisabled: {\n color: \"#e5e7eb\",\n },\n});\n"]}
1
+ {"version":3,"sources":["../src/utils/rnStyleToWebStyle.ts","../src/components/Button.tsx"],"names":["StyleSheet"],"mappings":"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","file":"index.js","sourcesContent":["import { StyleSheet, type StyleProp, type TextStyle, type ViewStyle } from \"react-native\";\nimport type { CSSProperties } from \"react\";\n\ntype RNStyle = StyleProp<ViewStyle> | StyleProp<TextStyle>;\n\n/**\n * Converts React Native StyleSheet values to CSS properties for DOM elements.\n * Required when rendering native HTML on web (browsers ignore paddingVertical, etc.).\n */\nexport function rnStyleToWebStyle(style: RNStyle): CSSProperties {\n const flat = StyleSheet.flatten(style);\n if (!flat) return {};\n\n const css: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(flat)) {\n if (value === undefined || value === null) continue;\n\n switch (key) {\n case \"paddingVertical\":\n css.paddingTop = value;\n css.paddingBottom = value;\n break;\n case \"paddingHorizontal\":\n css.paddingLeft = value;\n css.paddingRight = value;\n break;\n case \"marginVertical\":\n css.marginTop = value;\n css.marginBottom = value;\n break;\n case \"marginHorizontal\":\n css.marginLeft = value;\n css.marginRight = value;\n break;\n default:\n css[key] = value;\n }\n }\n\n if (css.alignItems || css.justifyContent || css.flexDirection) {\n css.display = css.display ?? \"flex\";\n }\n\n return css as CSSProperties;\n}\n","import React, { createElement } from \"react\";\nimport {\n Platform,\n StyleSheet,\n Text,\n TouchableOpacity,\n type GestureResponderEvent,\n type StyleProp,\n type TextStyle,\n type ViewStyle,\n} from \"react-native\";\nimport { rnStyleToWebStyle } from \"../utils/rnStyleToWebStyle\";\n\nexport interface ButtonProps {\n title: string;\n onPress: (event: GestureResponderEvent) => void;\n disabled?: boolean;\n /** Additional container styles (works on web and native). */\n style?: StyleProp<ViewStyle>;\n /** Additional label styles (works on web and native). */\n textStyle?: StyleProp<TextStyle>;\n /**\n * CSS class names for the container (web only uses DOM `<button>` for Tailwind).\n * On native: ignored unless using NativeWind with cssInterop.\n */\n className?: string;\n /**\n * CSS class names for the label (web only).\n * On native: ignored unless using NativeWind with cssInterop.\n */\n textClassName?: string;\n}\n\nexport function Button({\n title,\n onPress,\n disabled = false,\n style,\n textStyle,\n className,\n textClassName,\n}: ButtonProps) {\n const containerStyle = [styles.button, disabled && styles.buttonDisabled, style];\n const labelStyle = [styles.text, disabled && styles.textDisabled, textStyle];\n\n const useWebDom =\n Platform.OS === \"web\" && (className != null || textClassName != null);\n\n if (useWebDom) {\n return createElement(\n \"button\",\n {\n type: \"button\",\n className,\n style: rnStyleToWebStyle(containerStyle),\n disabled,\n onClick: (event: React.MouseEvent<HTMLButtonElement>) => {\n onPress(event as unknown as GestureResponderEvent);\n },\n \"aria-disabled\": disabled,\n },\n createElement(\n \"span\",\n {\n className: textClassName,\n style: rnStyleToWebStyle(labelStyle),\n },\n title,\n ),\n );\n }\n\n return (\n <TouchableOpacity\n style={containerStyle}\n onPress={onPress}\n disabled={disabled}\n activeOpacity={0.7}\n accessibilityRole=\"button\"\n accessibilityState={{ disabled }}\n >\n <Text style={labelStyle}>{title}</Text>\n </TouchableOpacity>\n );\n}\n\nconst styles = StyleSheet.create({\n button: {\n backgroundColor: \"#2563eb\",\n paddingVertical: 12,\n paddingHorizontal: 24,\n borderRadius: 8,\n alignItems: \"center\",\n justifyContent: \"center\",\n minWidth: 120,\n borderWidth: 0,\n },\n buttonDisabled: {\n backgroundColor: \"#93c5fd\",\n opacity: 0.7,\n },\n text: {\n color: \"#ffffff\",\n fontSize: 16,\n fontWeight: \"600\",\n },\n textDisabled: {\n color: \"#e5e7eb\",\n },\n});\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scripso-homepad/ui",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "type": "module",
5
5
  "description": "Cross-platform UI components for Homepad (React Web + React Native)",
6
6
  "license": "MIT",
@@ -9,6 +9,7 @@ import {
9
9
  type TextStyle,
10
10
  type ViewStyle,
11
11
  } from "react-native";
12
+ import { rnStyleToWebStyle } from "../utils/rnStyleToWebStyle";
12
13
 
13
14
  export interface ButtonProps {
14
15
  title: string;
@@ -19,14 +20,12 @@ export interface ButtonProps {
19
20
  /** Additional label styles (works on web and native). */
20
21
  textStyle?: StyleProp<TextStyle>;
21
22
  /**
22
- * CSS class names for the container.
23
- * On web: applied to the underlying `<button>` element (Tailwind works).
23
+ * CSS class names for the container (web only — uses DOM `<button>` for Tailwind).
24
24
  * On native: ignored unless using NativeWind with cssInterop.
25
25
  */
26
26
  className?: string;
27
27
  /**
28
- * CSS class names for the label.
29
- * On web: applied to the underlying `<span>` element (Tailwind works).
28
+ * CSS class names for the label (web only).
30
29
  * On native: ignored unless using NativeWind with cssInterop.
31
30
  */
32
31
  textClassName?: string;
@@ -44,13 +43,16 @@ export function Button({
44
43
  const containerStyle = [styles.button, disabled && styles.buttonDisabled, style];
45
44
  const labelStyle = [styles.text, disabled && styles.textDisabled, textStyle];
46
45
 
47
- if (Platform.OS === "web") {
46
+ const useWebDom =
47
+ Platform.OS === "web" && (className != null || textClassName != null);
48
+
49
+ if (useWebDom) {
48
50
  return createElement(
49
51
  "button",
50
52
  {
51
53
  type: "button",
52
54
  className,
53
- style: StyleSheet.flatten(containerStyle),
55
+ style: rnStyleToWebStyle(containerStyle),
54
56
  disabled,
55
57
  onClick: (event: React.MouseEvent<HTMLButtonElement>) => {
56
58
  onPress(event as unknown as GestureResponderEvent);
@@ -61,7 +63,7 @@ export function Button({
61
63
  "span",
62
64
  {
63
65
  className: textClassName,
64
- style: StyleSheet.flatten(labelStyle),
66
+ style: rnStyleToWebStyle(labelStyle),
65
67
  },
66
68
  title,
67
69
  ),
@@ -0,0 +1,46 @@
1
+ import { StyleSheet, type StyleProp, type TextStyle, type ViewStyle } from "react-native";
2
+ import type { CSSProperties } from "react";
3
+
4
+ type RNStyle = StyleProp<ViewStyle> | StyleProp<TextStyle>;
5
+
6
+ /**
7
+ * Converts React Native StyleSheet values to CSS properties for DOM elements.
8
+ * Required when rendering native HTML on web (browsers ignore paddingVertical, etc.).
9
+ */
10
+ export function rnStyleToWebStyle(style: RNStyle): CSSProperties {
11
+ const flat = StyleSheet.flatten(style);
12
+ if (!flat) return {};
13
+
14
+ const css: Record<string, unknown> = {};
15
+
16
+ for (const [key, value] of Object.entries(flat)) {
17
+ if (value === undefined || value === null) continue;
18
+
19
+ switch (key) {
20
+ case "paddingVertical":
21
+ css.paddingTop = value;
22
+ css.paddingBottom = value;
23
+ break;
24
+ case "paddingHorizontal":
25
+ css.paddingLeft = value;
26
+ css.paddingRight = value;
27
+ break;
28
+ case "marginVertical":
29
+ css.marginTop = value;
30
+ css.marginBottom = value;
31
+ break;
32
+ case "marginHorizontal":
33
+ css.marginLeft = value;
34
+ css.marginRight = value;
35
+ break;
36
+ default:
37
+ css[key] = value;
38
+ }
39
+ }
40
+
41
+ if (css.alignItems || css.justifyContent || css.flexDirection) {
42
+ css.display = css.display ?? "flex";
43
+ }
44
+
45
+ return css as CSSProperties;
46
+ }