@scripso-homepad/ui 0.3.3 → 0.3.5

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/README.md CHANGED
@@ -92,15 +92,28 @@ export function Example() {
92
92
 
93
93
  ### `Button`
94
94
 
95
- | Prop | Type | Required | Default | Description |
96
- | --------------- | ---------------------- | -------- | ------- | -------------------------------------------------------- |
97
- | `title` | `string` | Yes | — | Button label text |
98
- | `onPress` | `() => void` | Yes | — | Press handler |
99
- | `disabled` | `boolean` | No | `false` | Disables interaction |
100
- | `style` | `StyleProp<ViewStyle>` | No | | Extra container styles (web + native) |
101
- | `textStyle` | `StyleProp<TextStyle>` | No | | Extra label styles (web + native) |
102
- | `className` | `string` | No | — | CSS/Tailwind classes for container (web / NativeWind) |
103
- | `textClassName` | `string` | No | — | CSS/Tailwind classes for label (web / NativeWind) |
95
+ | Prop | Type | Required | Default | Description |
96
+ | --------------- | ------------------------------------------------- | -------- | ----------- | -------------------------------------------------------- |
97
+ | `title` | `string` | Yes | — | Button label text |
98
+ | `onPress` | `() => void` | Yes | — | Press handler |
99
+ | `disabled` | `boolean` | No | `false` | Disables interaction |
100
+ | `variant` | `"primary" \| "secondary" \| "outline" \| "ghost"` | No | `"primary"` | Visual style preset |
101
+ | `size` | `"small" \| "medium" \| "large"` | No | `"medium"` | Size preset |
102
+ | `style` | `StyleProp<ViewStyle>` | No | — | Extra container styles (web + native) |
103
+ | `textStyle` | `StyleProp<TextStyle>` | No | — | Extra label styles (web + native) |
104
+ | `className` | `string` | No | — | CSS/Tailwind classes for container (web / NativeWind) |
105
+ | `textClassName` | `string` | No | — | CSS/Tailwind classes for label (web / NativeWind) |
106
+
107
+ #### Variants and sizes
108
+
109
+ ```tsx
110
+ <Button title="Save" onPress={handleSave} variant="primary" size="medium" />
111
+ <Button title="Cancel" onPress={handleCancel} variant="outline" size="small" />
112
+ <Button title="Delete" onPress={handleDelete} variant="secondary" size="large" />
113
+ <Button title="More" onPress={handleMore} variant="ghost" />
114
+ ```
115
+
116
+ Preview all combinations in Storybook: `npm run storybook` → **AllVariants**, **AllSizes**, **VariantMatrix**.
104
117
 
105
118
  #### Custom styles (React Native `style`)
106
119
 
package/dist/index.cjs CHANGED
@@ -36,6 +36,8 @@ function Button({
36
36
  title,
37
37
  onPress,
38
38
  disabled = false,
39
+ variant = "primary",
40
+ size = "medium",
39
41
  style,
40
42
  textStyle,
41
43
  className,
@@ -45,8 +47,20 @@ function Button({
45
47
  const textRef = react.useRef(null);
46
48
  useApplyWebClassName(containerRef, className);
47
49
  useApplyWebClassName(textRef, textClassName);
48
- const containerStyle = [styles.button, disabled && styles.buttonDisabled, style];
49
- const labelStyle = [styles.text, disabled && styles.textDisabled, textStyle];
50
+ const containerStyle = [
51
+ styles.base,
52
+ variantStyles[variant],
53
+ sizeStyles[size],
54
+ disabled && disabledVariantStyles[variant],
55
+ style
56
+ ];
57
+ const labelStyle = [
58
+ textBaseStyles.base,
59
+ textVariantStyles[variant],
60
+ textSizeStyles[size],
61
+ disabled && textDisabledStyles[variant],
62
+ textStyle
63
+ ];
50
64
  return /* @__PURE__ */ jsxRuntime.jsx(
51
65
  reactNative.TouchableOpacity,
52
66
  {
@@ -62,27 +76,105 @@ function Button({
62
76
  );
63
77
  }
64
78
  var styles = reactNative.StyleSheet.create({
65
- button: {
66
- backgroundColor: "#2563eb",
67
- paddingVertical: 12,
68
- paddingHorizontal: 24,
69
- borderRadius: 8,
79
+ base: {
70
80
  alignItems: "center",
71
81
  justifyContent: "center",
72
- minWidth: 120,
82
+ borderRadius: 8,
73
83
  borderWidth: 0
84
+ }
85
+ });
86
+ var variantStyles = reactNative.StyleSheet.create({
87
+ primary: {
88
+ backgroundColor: "#2563eb"
89
+ },
90
+ secondary: {
91
+ backgroundColor: "#4b5563"
92
+ },
93
+ outline: {
94
+ backgroundColor: "transparent",
95
+ borderWidth: 1,
96
+ borderColor: "#2563eb"
97
+ },
98
+ ghost: {
99
+ backgroundColor: "transparent"
100
+ }
101
+ });
102
+ var sizeStyles = reactNative.StyleSheet.create({
103
+ small: {
104
+ paddingVertical: 8,
105
+ paddingHorizontal: 16,
106
+ minWidth: 96
107
+ },
108
+ medium: {
109
+ paddingVertical: 12,
110
+ paddingHorizontal: 24,
111
+ minWidth: 120
74
112
  },
75
- buttonDisabled: {
113
+ large: {
114
+ paddingVertical: 16,
115
+ paddingHorizontal: 32,
116
+ minWidth: 160
117
+ }
118
+ });
119
+ var disabledVariantStyles = reactNative.StyleSheet.create({
120
+ primary: {
76
121
  backgroundColor: "#93c5fd",
77
122
  opacity: 0.7
78
123
  },
79
- text: {
80
- color: "#ffffff",
81
- fontSize: 16,
124
+ secondary: {
125
+ backgroundColor: "#9ca3af",
126
+ opacity: 0.7
127
+ },
128
+ outline: {
129
+ borderColor: "#93c5fd",
130
+ opacity: 0.7
131
+ },
132
+ ghost: {
133
+ opacity: 0.5
134
+ }
135
+ });
136
+ var textBaseStyles = reactNative.StyleSheet.create({
137
+ base: {
82
138
  fontWeight: "600"
139
+ }
140
+ });
141
+ var textVariantStyles = reactNative.StyleSheet.create({
142
+ primary: {
143
+ color: "#ffffff"
144
+ },
145
+ secondary: {
146
+ color: "#ffffff"
83
147
  },
84
- textDisabled: {
148
+ outline: {
149
+ color: "#2563eb"
150
+ },
151
+ ghost: {
152
+ color: "#2563eb"
153
+ }
154
+ });
155
+ var textSizeStyles = reactNative.StyleSheet.create({
156
+ small: {
157
+ fontSize: 14
158
+ },
159
+ medium: {
160
+ fontSize: 16
161
+ },
162
+ large: {
163
+ fontSize: 18
164
+ }
165
+ });
166
+ var textDisabledStyles = reactNative.StyleSheet.create({
167
+ primary: {
85
168
  color: "#e5e7eb"
169
+ },
170
+ secondary: {
171
+ color: "#f3f4f6"
172
+ },
173
+ outline: {
174
+ color: "#93c5fd"
175
+ },
176
+ ghost: {
177
+ color: "#93c5fd"
86
178
  }
87
179
  });
88
180
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/useApplyWebClassName.ts","../src/components/Button.tsx"],"names":["useLayoutEffect","Platform","useRef","jsx","TouchableOpacity","Text","StyleSheet"],"mappings":"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","file":"index.cjs","sourcesContent":["import { useLayoutEffect } from \"react\";\nimport { Platform } from \"react-native\";\n\ninterface ClassListElement {\n classList: {\n add: (...classes: string[]) => void;\n remove: (...classes: string[]) => void;\n };\n}\n\nfunction hasClassList(node: unknown): node is ClassListElement {\n return (\n typeof node === \"object\" &&\n node !== null &&\n \"classList\" in node &&\n typeof (node as ClassListElement).classList?.add === \"function\"\n );\n}\n\nfunction resolveWebElement(ref: React.RefObject<unknown>): ClassListElement | null {\n const node = ref.current;\n if (!node) return null;\n\n if (hasClassList(node)) return node;\n\n const host = node as {\n _touchableNode?: unknown;\n getScrollableNode?: () => unknown;\n };\n\n if (hasClassList(host._touchableNode)) return host._touchableNode;\n\n if (typeof host.getScrollableNode === \"function\") {\n const scrollNode = host.getScrollableNode();\n if (hasClassList(scrollNode)) return scrollNode;\n }\n\n return null;\n}\n\n/**\n * Applies CSS class names to the underlying DOM node on web.\n * Keeps TouchableOpacity/Text as the render path so default RN styles stay intact.\n */\nexport function useApplyWebClassName(\n ref: React.RefObject<unknown>,\n className?: string,\n): void {\n useLayoutEffect(() => {\n if (Platform.OS !== \"web\" || !className?.trim()) return;\n\n const element = resolveWebElement(ref);\n if (!element) return;\n\n const classes = className.trim().split(/\\s+/);\n element.classList.add(...classes);\n\n return () => {\n element.classList.remove(...classes);\n };\n }, [ref, className]);\n}\n","import { useRef, type ComponentRef } from \"react\";\nimport {\n StyleSheet,\n Text,\n TouchableOpacity,\n type GestureResponderEvent,\n type StyleProp,\n type TextStyle,\n type ViewStyle,\n} from \"react-native\";\nimport { useApplyWebClassName } from \"../utils/useApplyWebClassName\";\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: applied to the same DOM node as default styles).\n * On native: ignored unless using NativeWind with cssInterop.\n */\n className?: string;\n /**\n * CSS class names for the label (web).\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 containerRef = useRef<ComponentRef<typeof TouchableOpacity>>(null);\n const textRef = useRef<ComponentRef<typeof Text>>(null);\n\n useApplyWebClassName(containerRef, className);\n useApplyWebClassName(textRef, textClassName);\n\n const containerStyle = [styles.button, disabled && styles.buttonDisabled, style];\n const labelStyle = [styles.text, disabled && styles.textDisabled, textStyle];\n\n return (\n <TouchableOpacity\n ref={containerRef}\n style={containerStyle}\n onPress={onPress}\n disabled={disabled}\n activeOpacity={0.7}\n accessibilityRole=\"button\"\n accessibilityState={{ disabled }}\n >\n <Text ref={textRef} style={labelStyle}>\n {title}\n </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/useApplyWebClassName.ts","../src/components/Button.tsx"],"names":["useLayoutEffect","Platform","useRef","jsx","TouchableOpacity","Text","StyleSheet"],"mappings":"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","file":"index.cjs","sourcesContent":["import { useLayoutEffect } from \"react\";\nimport { Platform } from \"react-native\";\n\ninterface ClassListElement {\n classList: {\n add: (...classes: string[]) => void;\n remove: (...classes: string[]) => void;\n };\n}\n\nfunction hasClassList(node: unknown): node is ClassListElement {\n return (\n typeof node === \"object\" &&\n node !== null &&\n \"classList\" in node &&\n typeof (node as ClassListElement).classList?.add === \"function\"\n );\n}\n\nfunction resolveWebElement(ref: React.RefObject<unknown>): ClassListElement | null {\n const node = ref.current;\n if (!node) return null;\n\n if (hasClassList(node)) return node;\n\n const host = node as {\n _touchableNode?: unknown;\n getScrollableNode?: () => unknown;\n };\n\n if (hasClassList(host._touchableNode)) return host._touchableNode;\n\n if (typeof host.getScrollableNode === \"function\") {\n const scrollNode = host.getScrollableNode();\n if (hasClassList(scrollNode)) return scrollNode;\n }\n\n return null;\n}\n\n/**\n * Applies CSS class names to the underlying DOM node on web.\n * Keeps TouchableOpacity/Text as the render path so default RN styles stay intact.\n */\nexport function useApplyWebClassName(\n ref: React.RefObject<unknown>,\n className?: string,\n): void {\n useLayoutEffect(() => {\n if (Platform.OS !== \"web\" || !className?.trim()) return;\n\n const element = resolveWebElement(ref);\n if (!element) return;\n\n const classes = className.trim().split(/\\s+/);\n element.classList.add(...classes);\n\n return () => {\n element.classList.remove(...classes);\n };\n }, [ref, className]);\n}\n","import { useRef, type ComponentRef } from \"react\";\nimport {\n StyleSheet,\n Text,\n TouchableOpacity,\n type GestureResponderEvent,\n type StyleProp,\n type TextStyle,\n type ViewStyle,\n} from \"react-native\";\nimport { useApplyWebClassName } from \"../utils/useApplyWebClassName\";\n\nexport type ButtonVariant = \"primary\" | \"secondary\" | \"outline\" | \"ghost\";\nexport type ButtonSize = \"small\" | \"medium\" | \"large\";\n\nexport interface ButtonProps {\n title: string;\n onPress: (event: GestureResponderEvent) => void;\n disabled?: boolean;\n /** Visual style preset. */\n variant?: ButtonVariant;\n /** Size preset. */\n size?: ButtonSize;\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: applied to the same DOM node as default styles).\n * On native: ignored unless using NativeWind with cssInterop.\n */\n className?: string;\n /**\n * CSS class names for the label (web).\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 variant = \"primary\",\n size = \"medium\",\n style,\n textStyle,\n className,\n textClassName,\n}: ButtonProps) {\n const containerRef = useRef<ComponentRef<typeof TouchableOpacity>>(null);\n const textRef = useRef<ComponentRef<typeof Text>>(null);\n\n useApplyWebClassName(containerRef, className);\n useApplyWebClassName(textRef, textClassName);\n\n const containerStyle = [\n styles.base,\n variantStyles[variant],\n sizeStyles[size],\n disabled && disabledVariantStyles[variant],\n style,\n ];\n\n const labelStyle = [\n textBaseStyles.base,\n textVariantStyles[variant],\n textSizeStyles[size],\n disabled && textDisabledStyles[variant],\n textStyle,\n ];\n\n return (\n <TouchableOpacity\n ref={containerRef}\n style={containerStyle}\n onPress={onPress}\n disabled={disabled}\n activeOpacity={0.7}\n accessibilityRole=\"button\"\n accessibilityState={{ disabled }}\n >\n <Text ref={textRef} style={labelStyle}>\n {title}\n </Text>\n </TouchableOpacity>\n );\n}\n\nconst styles = StyleSheet.create({\n base: {\n alignItems: \"center\",\n justifyContent: \"center\",\n borderRadius: 8,\n borderWidth: 0,\n },\n});\n\nconst variantStyles = StyleSheet.create({\n primary: {\n backgroundColor: \"#2563eb\",\n },\n secondary: {\n backgroundColor: \"#4b5563\",\n },\n outline: {\n backgroundColor: \"transparent\",\n borderWidth: 1,\n borderColor: \"#2563eb\",\n },\n ghost: {\n backgroundColor: \"transparent\",\n },\n});\n\nconst sizeStyles = StyleSheet.create({\n small: {\n paddingVertical: 8,\n paddingHorizontal: 16,\n minWidth: 96,\n },\n medium: {\n paddingVertical: 12,\n paddingHorizontal: 24,\n minWidth: 120,\n },\n large: {\n paddingVertical: 16,\n paddingHorizontal: 32,\n minWidth: 160,\n },\n});\n\nconst disabledVariantStyles = StyleSheet.create({\n primary: {\n backgroundColor: \"#93c5fd\",\n opacity: 0.7,\n },\n secondary: {\n backgroundColor: \"#9ca3af\",\n opacity: 0.7,\n },\n outline: {\n borderColor: \"#93c5fd\",\n opacity: 0.7,\n },\n ghost: {\n opacity: 0.5,\n },\n});\n\nconst textBaseStyles = StyleSheet.create({\n base: {\n fontWeight: \"600\",\n },\n});\n\nconst textVariantStyles = StyleSheet.create({\n primary: {\n color: \"#ffffff\",\n },\n secondary: {\n color: \"#ffffff\",\n },\n outline: {\n color: \"#2563eb\",\n },\n ghost: {\n color: \"#2563eb\",\n },\n});\n\nconst textSizeStyles = StyleSheet.create({\n small: {\n fontSize: 14,\n },\n medium: {\n fontSize: 16,\n },\n large: {\n fontSize: 18,\n },\n});\n\nconst textDisabledStyles = StyleSheet.create({\n primary: {\n color: \"#e5e7eb\",\n },\n secondary: {\n color: \"#f3f4f6\",\n },\n outline: {\n color: \"#93c5fd\",\n },\n ghost: {\n color: \"#93c5fd\",\n },\n});\n"]}
package/dist/index.d.cts CHANGED
@@ -1,10 +1,16 @@
1
1
  import * as react from 'react';
2
2
  import { GestureResponderEvent, StyleProp, ViewStyle, TextStyle } from 'react-native';
3
3
 
4
+ type ButtonVariant = "primary" | "secondary" | "outline" | "ghost";
5
+ type ButtonSize = "small" | "medium" | "large";
4
6
  interface ButtonProps {
5
7
  title: string;
6
8
  onPress: (event: GestureResponderEvent) => void;
7
9
  disabled?: boolean;
10
+ /** Visual style preset. */
11
+ variant?: ButtonVariant;
12
+ /** Size preset. */
13
+ size?: ButtonSize;
8
14
  /** Additional container styles (works on web and native). */
9
15
  style?: StyleProp<ViewStyle>;
10
16
  /** Additional label styles (works on web and native). */
@@ -20,6 +26,6 @@ interface ButtonProps {
20
26
  */
21
27
  textClassName?: string;
22
28
  }
23
- declare function Button({ title, onPress, disabled, style, textStyle, className, textClassName, }: ButtonProps): react.JSX.Element;
29
+ declare function Button({ title, onPress, disabled, variant, size, style, textStyle, className, textClassName, }: ButtonProps): react.JSX.Element;
24
30
 
25
- export { Button, type ButtonProps };
31
+ export { Button, type ButtonProps, type ButtonSize, type ButtonVariant };
package/dist/index.d.ts CHANGED
@@ -1,10 +1,16 @@
1
1
  import * as react from 'react';
2
2
  import { GestureResponderEvent, StyleProp, ViewStyle, TextStyle } from 'react-native';
3
3
 
4
+ type ButtonVariant = "primary" | "secondary" | "outline" | "ghost";
5
+ type ButtonSize = "small" | "medium" | "large";
4
6
  interface ButtonProps {
5
7
  title: string;
6
8
  onPress: (event: GestureResponderEvent) => void;
7
9
  disabled?: boolean;
10
+ /** Visual style preset. */
11
+ variant?: ButtonVariant;
12
+ /** Size preset. */
13
+ size?: ButtonSize;
8
14
  /** Additional container styles (works on web and native). */
9
15
  style?: StyleProp<ViewStyle>;
10
16
  /** Additional label styles (works on web and native). */
@@ -20,6 +26,6 @@ interface ButtonProps {
20
26
  */
21
27
  textClassName?: string;
22
28
  }
23
- declare function Button({ title, onPress, disabled, style, textStyle, className, textClassName, }: ButtonProps): react.JSX.Element;
29
+ declare function Button({ title, onPress, disabled, variant, size, style, textStyle, className, textClassName, }: ButtonProps): react.JSX.Element;
24
30
 
25
- export { Button, type ButtonProps };
31
+ export { Button, type ButtonProps, type ButtonSize, type ButtonVariant };
package/dist/index.js CHANGED
@@ -34,6 +34,8 @@ function Button({
34
34
  title,
35
35
  onPress,
36
36
  disabled = false,
37
+ variant = "primary",
38
+ size = "medium",
37
39
  style,
38
40
  textStyle,
39
41
  className,
@@ -43,8 +45,20 @@ function Button({
43
45
  const textRef = useRef(null);
44
46
  useApplyWebClassName(containerRef, className);
45
47
  useApplyWebClassName(textRef, textClassName);
46
- const containerStyle = [styles.button, disabled && styles.buttonDisabled, style];
47
- const labelStyle = [styles.text, disabled && styles.textDisabled, textStyle];
48
+ const containerStyle = [
49
+ styles.base,
50
+ variantStyles[variant],
51
+ sizeStyles[size],
52
+ disabled && disabledVariantStyles[variant],
53
+ style
54
+ ];
55
+ const labelStyle = [
56
+ textBaseStyles.base,
57
+ textVariantStyles[variant],
58
+ textSizeStyles[size],
59
+ disabled && textDisabledStyles[variant],
60
+ textStyle
61
+ ];
48
62
  return /* @__PURE__ */ jsx(
49
63
  TouchableOpacity,
50
64
  {
@@ -60,27 +74,105 @@ function Button({
60
74
  );
61
75
  }
62
76
  var styles = StyleSheet.create({
63
- button: {
64
- backgroundColor: "#2563eb",
65
- paddingVertical: 12,
66
- paddingHorizontal: 24,
67
- borderRadius: 8,
77
+ base: {
68
78
  alignItems: "center",
69
79
  justifyContent: "center",
70
- minWidth: 120,
80
+ borderRadius: 8,
71
81
  borderWidth: 0
82
+ }
83
+ });
84
+ var variantStyles = StyleSheet.create({
85
+ primary: {
86
+ backgroundColor: "#2563eb"
87
+ },
88
+ secondary: {
89
+ backgroundColor: "#4b5563"
90
+ },
91
+ outline: {
92
+ backgroundColor: "transparent",
93
+ borderWidth: 1,
94
+ borderColor: "#2563eb"
95
+ },
96
+ ghost: {
97
+ backgroundColor: "transparent"
98
+ }
99
+ });
100
+ var sizeStyles = StyleSheet.create({
101
+ small: {
102
+ paddingVertical: 8,
103
+ paddingHorizontal: 16,
104
+ minWidth: 96
105
+ },
106
+ medium: {
107
+ paddingVertical: 12,
108
+ paddingHorizontal: 24,
109
+ minWidth: 120
72
110
  },
73
- buttonDisabled: {
111
+ large: {
112
+ paddingVertical: 16,
113
+ paddingHorizontal: 32,
114
+ minWidth: 160
115
+ }
116
+ });
117
+ var disabledVariantStyles = StyleSheet.create({
118
+ primary: {
74
119
  backgroundColor: "#93c5fd",
75
120
  opacity: 0.7
76
121
  },
77
- text: {
78
- color: "#ffffff",
79
- fontSize: 16,
122
+ secondary: {
123
+ backgroundColor: "#9ca3af",
124
+ opacity: 0.7
125
+ },
126
+ outline: {
127
+ borderColor: "#93c5fd",
128
+ opacity: 0.7
129
+ },
130
+ ghost: {
131
+ opacity: 0.5
132
+ }
133
+ });
134
+ var textBaseStyles = StyleSheet.create({
135
+ base: {
80
136
  fontWeight: "600"
137
+ }
138
+ });
139
+ var textVariantStyles = StyleSheet.create({
140
+ primary: {
141
+ color: "#ffffff"
142
+ },
143
+ secondary: {
144
+ color: "#ffffff"
81
145
  },
82
- textDisabled: {
146
+ outline: {
147
+ color: "#2563eb"
148
+ },
149
+ ghost: {
150
+ color: "#2563eb"
151
+ }
152
+ });
153
+ var textSizeStyles = StyleSheet.create({
154
+ small: {
155
+ fontSize: 14
156
+ },
157
+ medium: {
158
+ fontSize: 16
159
+ },
160
+ large: {
161
+ fontSize: 18
162
+ }
163
+ });
164
+ var textDisabledStyles = StyleSheet.create({
165
+ primary: {
83
166
  color: "#e5e7eb"
167
+ },
168
+ secondary: {
169
+ color: "#f3f4f6"
170
+ },
171
+ outline: {
172
+ color: "#93c5fd"
173
+ },
174
+ ghost: {
175
+ color: "#93c5fd"
84
176
  }
85
177
  });
86
178
 
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/useApplyWebClassName.ts","../src/components/Button.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { useLayoutEffect } from \"react\";\nimport { Platform } from \"react-native\";\n\ninterface ClassListElement {\n classList: {\n add: (...classes: string[]) => void;\n remove: (...classes: string[]) => void;\n };\n}\n\nfunction hasClassList(node: unknown): node is ClassListElement {\n return (\n typeof node === \"object\" &&\n node !== null &&\n \"classList\" in node &&\n typeof (node as ClassListElement).classList?.add === \"function\"\n );\n}\n\nfunction resolveWebElement(ref: React.RefObject<unknown>): ClassListElement | null {\n const node = ref.current;\n if (!node) return null;\n\n if (hasClassList(node)) return node;\n\n const host = node as {\n _touchableNode?: unknown;\n getScrollableNode?: () => unknown;\n };\n\n if (hasClassList(host._touchableNode)) return host._touchableNode;\n\n if (typeof host.getScrollableNode === \"function\") {\n const scrollNode = host.getScrollableNode();\n if (hasClassList(scrollNode)) return scrollNode;\n }\n\n return null;\n}\n\n/**\n * Applies CSS class names to the underlying DOM node on web.\n * Keeps TouchableOpacity/Text as the render path so default RN styles stay intact.\n */\nexport function useApplyWebClassName(\n ref: React.RefObject<unknown>,\n className?: string,\n): void {\n useLayoutEffect(() => {\n if (Platform.OS !== \"web\" || !className?.trim()) return;\n\n const element = resolveWebElement(ref);\n if (!element) return;\n\n const classes = className.trim().split(/\\s+/);\n element.classList.add(...classes);\n\n return () => {\n element.classList.remove(...classes);\n };\n }, [ref, className]);\n}\n","import { useRef, type ComponentRef } from \"react\";\nimport {\n StyleSheet,\n Text,\n TouchableOpacity,\n type GestureResponderEvent,\n type StyleProp,\n type TextStyle,\n type ViewStyle,\n} from \"react-native\";\nimport { useApplyWebClassName } from \"../utils/useApplyWebClassName\";\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: applied to the same DOM node as default styles).\n * On native: ignored unless using NativeWind with cssInterop.\n */\n className?: string;\n /**\n * CSS class names for the label (web).\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 containerRef = useRef<ComponentRef<typeof TouchableOpacity>>(null);\n const textRef = useRef<ComponentRef<typeof Text>>(null);\n\n useApplyWebClassName(containerRef, className);\n useApplyWebClassName(textRef, textClassName);\n\n const containerStyle = [styles.button, disabled && styles.buttonDisabled, style];\n const labelStyle = [styles.text, disabled && styles.textDisabled, textStyle];\n\n return (\n <TouchableOpacity\n ref={containerRef}\n style={containerStyle}\n onPress={onPress}\n disabled={disabled}\n activeOpacity={0.7}\n accessibilityRole=\"button\"\n accessibilityState={{ disabled }}\n >\n <Text ref={textRef} style={labelStyle}>\n {title}\n </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/useApplyWebClassName.ts","../src/components/Button.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { useLayoutEffect } from \"react\";\nimport { Platform } from \"react-native\";\n\ninterface ClassListElement {\n classList: {\n add: (...classes: string[]) => void;\n remove: (...classes: string[]) => void;\n };\n}\n\nfunction hasClassList(node: unknown): node is ClassListElement {\n return (\n typeof node === \"object\" &&\n node !== null &&\n \"classList\" in node &&\n typeof (node as ClassListElement).classList?.add === \"function\"\n );\n}\n\nfunction resolveWebElement(ref: React.RefObject<unknown>): ClassListElement | null {\n const node = ref.current;\n if (!node) return null;\n\n if (hasClassList(node)) return node;\n\n const host = node as {\n _touchableNode?: unknown;\n getScrollableNode?: () => unknown;\n };\n\n if (hasClassList(host._touchableNode)) return host._touchableNode;\n\n if (typeof host.getScrollableNode === \"function\") {\n const scrollNode = host.getScrollableNode();\n if (hasClassList(scrollNode)) return scrollNode;\n }\n\n return null;\n}\n\n/**\n * Applies CSS class names to the underlying DOM node on web.\n * Keeps TouchableOpacity/Text as the render path so default RN styles stay intact.\n */\nexport function useApplyWebClassName(\n ref: React.RefObject<unknown>,\n className?: string,\n): void {\n useLayoutEffect(() => {\n if (Platform.OS !== \"web\" || !className?.trim()) return;\n\n const element = resolveWebElement(ref);\n if (!element) return;\n\n const classes = className.trim().split(/\\s+/);\n element.classList.add(...classes);\n\n return () => {\n element.classList.remove(...classes);\n };\n }, [ref, className]);\n}\n","import { useRef, type ComponentRef } from \"react\";\nimport {\n StyleSheet,\n Text,\n TouchableOpacity,\n type GestureResponderEvent,\n type StyleProp,\n type TextStyle,\n type ViewStyle,\n} from \"react-native\";\nimport { useApplyWebClassName } from \"../utils/useApplyWebClassName\";\n\nexport type ButtonVariant = \"primary\" | \"secondary\" | \"outline\" | \"ghost\";\nexport type ButtonSize = \"small\" | \"medium\" | \"large\";\n\nexport interface ButtonProps {\n title: string;\n onPress: (event: GestureResponderEvent) => void;\n disabled?: boolean;\n /** Visual style preset. */\n variant?: ButtonVariant;\n /** Size preset. */\n size?: ButtonSize;\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: applied to the same DOM node as default styles).\n * On native: ignored unless using NativeWind with cssInterop.\n */\n className?: string;\n /**\n * CSS class names for the label (web).\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 variant = \"primary\",\n size = \"medium\",\n style,\n textStyle,\n className,\n textClassName,\n}: ButtonProps) {\n const containerRef = useRef<ComponentRef<typeof TouchableOpacity>>(null);\n const textRef = useRef<ComponentRef<typeof Text>>(null);\n\n useApplyWebClassName(containerRef, className);\n useApplyWebClassName(textRef, textClassName);\n\n const containerStyle = [\n styles.base,\n variantStyles[variant],\n sizeStyles[size],\n disabled && disabledVariantStyles[variant],\n style,\n ];\n\n const labelStyle = [\n textBaseStyles.base,\n textVariantStyles[variant],\n textSizeStyles[size],\n disabled && textDisabledStyles[variant],\n textStyle,\n ];\n\n return (\n <TouchableOpacity\n ref={containerRef}\n style={containerStyle}\n onPress={onPress}\n disabled={disabled}\n activeOpacity={0.7}\n accessibilityRole=\"button\"\n accessibilityState={{ disabled }}\n >\n <Text ref={textRef} style={labelStyle}>\n {title}\n </Text>\n </TouchableOpacity>\n );\n}\n\nconst styles = StyleSheet.create({\n base: {\n alignItems: \"center\",\n justifyContent: \"center\",\n borderRadius: 8,\n borderWidth: 0,\n },\n});\n\nconst variantStyles = StyleSheet.create({\n primary: {\n backgroundColor: \"#2563eb\",\n },\n secondary: {\n backgroundColor: \"#4b5563\",\n },\n outline: {\n backgroundColor: \"transparent\",\n borderWidth: 1,\n borderColor: \"#2563eb\",\n },\n ghost: {\n backgroundColor: \"transparent\",\n },\n});\n\nconst sizeStyles = StyleSheet.create({\n small: {\n paddingVertical: 8,\n paddingHorizontal: 16,\n minWidth: 96,\n },\n medium: {\n paddingVertical: 12,\n paddingHorizontal: 24,\n minWidth: 120,\n },\n large: {\n paddingVertical: 16,\n paddingHorizontal: 32,\n minWidth: 160,\n },\n});\n\nconst disabledVariantStyles = StyleSheet.create({\n primary: {\n backgroundColor: \"#93c5fd\",\n opacity: 0.7,\n },\n secondary: {\n backgroundColor: \"#9ca3af\",\n opacity: 0.7,\n },\n outline: {\n borderColor: \"#93c5fd\",\n opacity: 0.7,\n },\n ghost: {\n opacity: 0.5,\n },\n});\n\nconst textBaseStyles = StyleSheet.create({\n base: {\n fontWeight: \"600\",\n },\n});\n\nconst textVariantStyles = StyleSheet.create({\n primary: {\n color: \"#ffffff\",\n },\n secondary: {\n color: \"#ffffff\",\n },\n outline: {\n color: \"#2563eb\",\n },\n ghost: {\n color: \"#2563eb\",\n },\n});\n\nconst textSizeStyles = StyleSheet.create({\n small: {\n fontSize: 14,\n },\n medium: {\n fontSize: 16,\n },\n large: {\n fontSize: 18,\n },\n});\n\nconst textDisabledStyles = StyleSheet.create({\n primary: {\n color: \"#e5e7eb\",\n },\n secondary: {\n color: \"#f3f4f6\",\n },\n outline: {\n color: \"#93c5fd\",\n },\n ghost: {\n color: \"#93c5fd\",\n },\n});\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scripso-homepad/ui",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "type": "module",
5
5
  "description": "Cross-platform UI components for Homepad (React Web + React Native)",
6
6
  "license": "MIT",
@@ -1,5 +1,6 @@
1
1
  import type { Meta, StoryObj } from "@storybook/react";
2
2
  import { fn } from "@storybook/test";
3
+ import { View, StyleSheet } from "react-native";
3
4
  import { Button } from "./Button";
4
5
 
5
6
  const meta = {
@@ -9,9 +10,19 @@ const meta = {
9
10
  title: "Press me",
10
11
  onPress: fn(),
11
12
  disabled: false,
13
+ variant: "primary",
14
+ size: "medium",
12
15
  },
13
16
  argTypes: {
14
17
  onPress: { action: "pressed" },
18
+ variant: {
19
+ control: "select",
20
+ options: ["primary", "secondary", "outline", "ghost"],
21
+ },
22
+ size: {
23
+ control: "select",
24
+ options: ["small", "medium", "large"],
25
+ },
15
26
  },
16
27
  } satisfies Meta<typeof Button>;
17
28
 
@@ -20,47 +31,91 @@ type Story = StoryObj<typeof meta>;
20
31
 
21
32
  export const Default: Story = {};
22
33
 
34
+ export const Primary: Story = {
35
+ args: { variant: "primary", title: "Primary" },
36
+ };
37
+
38
+ export const Secondary: Story = {
39
+ args: { variant: "secondary", title: "Secondary" },
40
+ };
41
+
42
+ export const Outline: Story = {
43
+ args: { variant: "outline", title: "Outline" },
44
+ };
45
+
46
+ export const Ghost: Story = {
47
+ args: { variant: "ghost", title: "Ghost" },
48
+ };
49
+
50
+ export const Small: Story = {
51
+ args: { size: "small", title: "Small" },
52
+ };
53
+
54
+ export const Large: Story = {
55
+ args: { size: "large", title: "Large" },
56
+ };
57
+
23
58
  export const Disabled: Story = {
24
- args: {
25
- disabled: true,
26
- title: "Disabled",
27
- },
59
+ args: { disabled: true, title: "Disabled" },
28
60
  };
29
61
 
30
- export const LongLabel: Story = {
31
- args: {
32
- title: "Continue to next step",
33
- },
62
+ export const AllVariants: Story = {
63
+ render: () => (
64
+ <View style={storyStyles.column}>
65
+ <Button title="Primary" variant="primary" onPress={fn()} />
66
+ <Button title="Secondary" variant="secondary" onPress={fn()} />
67
+ <Button title="Outline" variant="outline" onPress={fn()} />
68
+ <Button title="Ghost" variant="ghost" onPress={fn()} />
69
+ </View>
70
+ ),
34
71
  };
35
72
 
36
- export const CustomStyle: Story = {
37
- args: {
38
- title: "Custom styles",
39
- style: {
40
- backgroundColor: "#dc2626",
41
- borderRadius: 999,
42
- paddingHorizontal: 32,
43
- },
44
- textStyle: {
45
- fontSize: 14,
46
- letterSpacing: 1,
47
- textTransform: "uppercase",
48
- },
49
- },
73
+ export const AllSizes: Story = {
74
+ render: () => (
75
+ <View style={storyStyles.column}>
76
+ <Button title="Small" size="small" onPress={fn()} />
77
+ <Button title="Medium" size="medium" onPress={fn()} />
78
+ <Button title="Large" size="large" onPress={fn()} />
79
+ </View>
80
+ ),
81
+ };
82
+
83
+ export const VariantMatrix: Story = {
84
+ render: () => (
85
+ <View style={storyStyles.grid}>
86
+ {(["primary", "secondary", "outline", "ghost"] as const).map((variant) => (
87
+ <View key={variant} style={storyStyles.column}>
88
+ {(["small", "medium", "large"] as const).map((size) => (
89
+ <Button
90
+ key={`${variant}-${size}`}
91
+ title={`${variant} ${size}`}
92
+ variant={variant}
93
+ size={size}
94
+ onPress={fn()}
95
+ />
96
+ ))}
97
+ </View>
98
+ ))}
99
+ </View>
100
+ ),
50
101
  };
51
102
 
52
103
  export const WithClassName: Story = {
53
104
  args: {
54
- title: "Tailwind classes",
55
- className: "rounded-full bg-violet-600 px-8 shadow-lg",
56
- textClassName: "text-sm font-bold uppercase tracking-wide",
57
- },
58
- parameters: {
59
- docs: {
60
- description: {
61
- story:
62
- "Pass Tailwind utility classes via className. Requires Tailwind in your web app (or NativeWind on native).",
63
- },
64
- },
105
+ title: "Custom Tailwind",
106
+ className: "!bg-violet-600 shadow-lg",
107
+ textClassName: "uppercase tracking-wide",
65
108
  },
66
109
  };
110
+
111
+ const storyStyles = StyleSheet.create({
112
+ column: {
113
+ gap: 12,
114
+ alignItems: "flex-start",
115
+ },
116
+ grid: {
117
+ flexDirection: "row",
118
+ flexWrap: "wrap",
119
+ gap: 24,
120
+ },
121
+ });
@@ -10,10 +10,17 @@ import {
10
10
  } from "react-native";
11
11
  import { useApplyWebClassName } from "../utils/useApplyWebClassName";
12
12
 
13
+ export type ButtonVariant = "primary" | "secondary" | "outline" | "ghost";
14
+ export type ButtonSize = "small" | "medium" | "large";
15
+
13
16
  export interface ButtonProps {
14
17
  title: string;
15
18
  onPress: (event: GestureResponderEvent) => void;
16
19
  disabled?: boolean;
20
+ /** Visual style preset. */
21
+ variant?: ButtonVariant;
22
+ /** Size preset. */
23
+ size?: ButtonSize;
17
24
  /** Additional container styles (works on web and native). */
18
25
  style?: StyleProp<ViewStyle>;
19
26
  /** Additional label styles (works on web and native). */
@@ -34,6 +41,8 @@ export function Button({
34
41
  title,
35
42
  onPress,
36
43
  disabled = false,
44
+ variant = "primary",
45
+ size = "medium",
37
46
  style,
38
47
  textStyle,
39
48
  className,
@@ -45,8 +54,21 @@ export function Button({
45
54
  useApplyWebClassName(containerRef, className);
46
55
  useApplyWebClassName(textRef, textClassName);
47
56
 
48
- const containerStyle = [styles.button, disabled && styles.buttonDisabled, style];
49
- const labelStyle = [styles.text, disabled && styles.textDisabled, textStyle];
57
+ const containerStyle = [
58
+ styles.base,
59
+ variantStyles[variant],
60
+ sizeStyles[size],
61
+ disabled && disabledVariantStyles[variant],
62
+ style,
63
+ ];
64
+
65
+ const labelStyle = [
66
+ textBaseStyles.base,
67
+ textVariantStyles[variant],
68
+ textSizeStyles[size],
69
+ disabled && textDisabledStyles[variant],
70
+ textStyle,
71
+ ];
50
72
 
51
73
  return (
52
74
  <TouchableOpacity
@@ -66,26 +88,111 @@ export function Button({
66
88
  }
67
89
 
68
90
  const styles = StyleSheet.create({
69
- button: {
91
+ base: {
92
+ alignItems: "center",
93
+ justifyContent: "center",
94
+ borderRadius: 8,
95
+ borderWidth: 0,
96
+ },
97
+ });
98
+
99
+ const variantStyles = StyleSheet.create({
100
+ primary: {
70
101
  backgroundColor: "#2563eb",
102
+ },
103
+ secondary: {
104
+ backgroundColor: "#4b5563",
105
+ },
106
+ outline: {
107
+ backgroundColor: "transparent",
108
+ borderWidth: 1,
109
+ borderColor: "#2563eb",
110
+ },
111
+ ghost: {
112
+ backgroundColor: "transparent",
113
+ },
114
+ });
115
+
116
+ const sizeStyles = StyleSheet.create({
117
+ small: {
118
+ paddingVertical: 8,
119
+ paddingHorizontal: 16,
120
+ minWidth: 96,
121
+ },
122
+ medium: {
71
123
  paddingVertical: 12,
72
124
  paddingHorizontal: 24,
73
- borderRadius: 8,
74
- alignItems: "center",
75
- justifyContent: "center",
76
125
  minWidth: 120,
77
- borderWidth: 0,
78
126
  },
79
- buttonDisabled: {
127
+ large: {
128
+ paddingVertical: 16,
129
+ paddingHorizontal: 32,
130
+ minWidth: 160,
131
+ },
132
+ });
133
+
134
+ const disabledVariantStyles = StyleSheet.create({
135
+ primary: {
80
136
  backgroundColor: "#93c5fd",
81
137
  opacity: 0.7,
82
138
  },
83
- text: {
139
+ secondary: {
140
+ backgroundColor: "#9ca3af",
141
+ opacity: 0.7,
142
+ },
143
+ outline: {
144
+ borderColor: "#93c5fd",
145
+ opacity: 0.7,
146
+ },
147
+ ghost: {
148
+ opacity: 0.5,
149
+ },
150
+ });
151
+
152
+ const textBaseStyles = StyleSheet.create({
153
+ base: {
154
+ fontWeight: "600",
155
+ },
156
+ });
157
+
158
+ const textVariantStyles = StyleSheet.create({
159
+ primary: {
84
160
  color: "#ffffff",
161
+ },
162
+ secondary: {
163
+ color: "#ffffff",
164
+ },
165
+ outline: {
166
+ color: "#2563eb",
167
+ },
168
+ ghost: {
169
+ color: "#2563eb",
170
+ },
171
+ });
172
+
173
+ const textSizeStyles = StyleSheet.create({
174
+ small: {
175
+ fontSize: 14,
176
+ },
177
+ medium: {
85
178
  fontSize: 16,
86
- fontWeight: "600",
87
179
  },
88
- textDisabled: {
180
+ large: {
181
+ fontSize: 18,
182
+ },
183
+ });
184
+
185
+ const textDisabledStyles = StyleSheet.create({
186
+ primary: {
89
187
  color: "#e5e7eb",
90
188
  },
189
+ secondary: {
190
+ color: "#f3f4f6",
191
+ },
192
+ outline: {
193
+ color: "#93c5fd",
194
+ },
195
+ ghost: {
196
+ color: "#93c5fd",
197
+ },
91
198
  });
package/src/index.ts CHANGED
@@ -1,2 +1,6 @@
1
1
  export { Button } from "./components/Button";
2
- export type { ButtonProps } from "./components/Button";
2
+ export type {
3
+ ButtonProps,
4
+ ButtonSize,
5
+ ButtonVariant,
6
+ } from "./components/Button";