onecart-ui 1.0.1 → 1.0.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.
Files changed (60) hide show
  1. package/README.md +64 -27
  2. package/dist/components/Button/Button.d.ts.map +1 -1
  3. package/dist/components/Button/index.d.ts +2 -2
  4. package/dist/components/Button/index.d.ts.map +1 -1
  5. package/dist/components/Icon/Icon.d.ts +40 -0
  6. package/dist/components/Icon/Icon.d.ts.map +1 -0
  7. package/dist/components/Icon/IconList.d.ts +21 -0
  8. package/dist/components/Icon/IconList.d.ts.map +1 -0
  9. package/dist/components/Icon/index.d.ts +6 -0
  10. package/dist/components/Icon/index.d.ts.map +1 -0
  11. package/dist/components/Typography/Body.d.ts +4 -0
  12. package/dist/components/Typography/Body.d.ts.map +1 -0
  13. package/dist/components/Typography/Display.d.ts +4 -0
  14. package/dist/components/Typography/Display.d.ts.map +1 -0
  15. package/dist/components/Typography/Heading.d.ts +4 -0
  16. package/dist/components/Typography/Heading.d.ts.map +1 -0
  17. package/dist/components/Typography/Utility.d.ts +4 -0
  18. package/dist/components/Typography/Utility.d.ts.map +1 -0
  19. package/dist/components/Typography/index.d.ts +4 -2
  20. package/dist/components/Typography/index.d.ts.map +1 -1
  21. package/dist/index.d.ts +4 -9
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.esm.js +1 -1
  24. package/dist/index.esm.js.map +1 -1
  25. package/dist/index.js +1 -1
  26. package/dist/index.js.map +1 -1
  27. package/dist/styles/tokens/typography.d.ts +11 -0
  28. package/dist/styles/tokens/typography.d.ts.map +1 -1
  29. package/dist/types/Button.d.ts +17 -40
  30. package/dist/types/Button.d.ts.map +1 -1
  31. package/dist/types/Typography.d.ts +20 -55
  32. package/dist/types/Typography.d.ts.map +1 -1
  33. package/package.json +2 -1
  34. package/src/components/Button/Button.tsx +235 -136
  35. package/src/components/Button/index.ts +7 -2
  36. package/src/components/Icon/Icon.tsx +113 -0
  37. package/src/components/Icon/IconList.tsx +110 -0
  38. package/src/components/Icon/icons.json +81 -0
  39. package/src/components/Icon/index.ts +5 -0
  40. package/src/components/Typography/Body.tsx +94 -0
  41. package/src/components/Typography/Display.tsx +79 -0
  42. package/src/components/Typography/Heading.tsx +110 -0
  43. package/src/components/Typography/Utility.tsx +107 -0
  44. package/src/components/Typography/index.ts +4 -7
  45. package/src/index.ts +8 -17
  46. package/src/styles/tokens/tokens-autocomplete-config.json +2 -2
  47. package/src/styles/tokens/tokens.css +1 -1
  48. package/src/styles/tokens/typography.ts +11 -0
  49. package/src/types/Button.ts +19 -48
  50. package/src/types/Typography.ts +21 -81
  51. package/src/components/Heading/Heading.tsx +0 -48
  52. package/src/components/Heading/index.ts +0 -2
  53. package/src/components/Paragraph/Paragraph.tsx +0 -93
  54. package/src/components/Paragraph/index.ts +0 -6
  55. package/src/components/Text/Text.tsx +0 -96
  56. package/src/components/Text/index.ts +0 -2
  57. package/src/components/Typography/Typography.tsx +0 -123
  58. package/src/types/Heading.ts +0 -27
  59. package/src/types/Paragraph.ts +0 -40
  60. package/src/types/Text.ts +0 -40
@@ -1,155 +1,254 @@
1
- import React from "react";
2
- import {
3
- TouchableOpacity,
4
- Text,
5
- ActivityIndicator,
6
- ViewStyle,
7
- TextStyle,
8
- } from "react-native";
9
- import { buttonTokens } from "../../styles/tokens";
10
- import { ButtonProps } from "../../types/Button";
1
+ import React, { useState } from "react";
2
+ import { ButtonProps, ButtonState } from "../../types/Button";
3
+ import { Icon } from "../Icon";
11
4
 
12
5
  export const Button: React.FC<ButtonProps> = ({
13
- children,
14
- variant = "primary",
15
- size = "md",
6
+ label,
7
+ type = "primary",
8
+ size = "large",
16
9
  disabled = false,
10
+ leftIcon,
11
+ rightIcon,
12
+ icon,
17
13
  fullWidth = false,
18
- loading = false,
19
14
  style,
20
- textStyle,
21
15
  accessibilityLabel,
22
16
  testID,
23
- onPress,
17
+ onClick,
18
+ className,
19
+ onMouseEnter,
20
+ onMouseLeave,
21
+ onMouseDown,
22
+ onMouseUp,
24
23
  }) => {
25
- // Calculate variant styles
26
- const variantStyles = React.useMemo(() => {
27
- try {
28
- const sizeConfig = buttonTokens.sizes[size];
29
- const variantConfig = buttonTokens[variant];
30
-
31
- const baseStyle: ViewStyle = {
32
- borderRadius: sizeConfig.borderRadius,
33
- borderWidth: buttonTokens.common.borderWidth,
34
- alignItems: "center",
35
- justifyContent: "center",
36
- flexDirection: "row",
37
- };
38
-
39
- return {
40
- ...baseStyle,
41
- backgroundColor: disabled
42
- ? variantConfig.background.disabled
43
- : variantConfig.background.default,
44
- borderColor: disabled
45
- ? variantConfig.border.disabled
46
- : variantConfig.border.default,
47
- };
48
- } catch (error) {
49
- // Fallback styles
50
- return {
51
- backgroundColor: variant === "primary" ? "#007AFF" : "transparent",
52
- borderColor: variant === "primary" ? "#007AFF" : "#E5E5EA",
53
- borderWidth: 1,
54
- borderRadius: 4,
55
- alignItems: "center",
56
- justifyContent: "center",
57
- flexDirection: "row",
58
- };
24
+ const isIconOnly = !!icon;
25
+
26
+ // Create icon components from icon names
27
+ const leftIconComponent = leftIcon ? (
28
+ <Icon name={leftIcon} size={size === "large" ? 20 : 16} />
29
+ ) : null;
30
+ const rightIconComponent = rightIcon ? (
31
+ <Icon name={rightIcon} size={size === "large" ? 20 : 16} />
32
+ ) : null;
33
+ const iconButton = icon ? (
34
+ <Icon name={icon} size={size === "large" ? 20 : 16} />
35
+ ) : null;
36
+
37
+ const [buttonState, setButtonState] = useState<ButtonState>(
38
+ disabled ? "disabled" : "default"
39
+ );
40
+
41
+ const hasBothIcons = leftIcon && rightIcon;
42
+
43
+ // Base button styles
44
+ const getBaseStyles = (): React.CSSProperties => {
45
+ const baseStyle: React.CSSProperties = {
46
+ display: isIconOnly ? "inline-flex" : "flex",
47
+ width: isIconOnly ? "auto" : fullWidth ? "100%" : "9.375rem",
48
+ height: size === "large" ? "2.75rem" : "2rem",
49
+ padding:
50
+ type === "ghost"
51
+ ? "var(--spacing-0, 0rem)"
52
+ : "var(--component-card-sm, 0.75rem)",
53
+ justifyContent: "center",
54
+ alignItems: "center",
55
+ gap: hasBothIcons
56
+ ? "var(--layout-spacing-inline-md, 1rem)"
57
+ : "var(--layout-spacing-inline-xs, 0.5rem)",
58
+ flexShrink: 0,
59
+ borderRadius: "var(--button-full, 62.4375rem)",
60
+ cursor: disabled ? "not-allowed" : "pointer",
61
+ opacity: disabled ? 0.6 : 1,
62
+ transition: "all 0.2s ease-in-out",
63
+ border: "none",
64
+ };
65
+
66
+ // Apply styles based on type and state
67
+ switch (type) {
68
+ case "primary":
69
+ switch (buttonState) {
70
+ case "default":
71
+ baseStyle.background = "var(--action-primary-default, #00DF88)";
72
+ break;
73
+ case "hover":
74
+ baseStyle.background = "var(--action-primary-hover, #4CE9AC)";
75
+ break;
76
+ case "active":
77
+ baseStyle.background = "var(--action-primary-active, #98F2CF)";
78
+ break;
79
+ case "disabled":
80
+ baseStyle.background = "var(--action-primary-disabled, #00DF88)";
81
+ baseStyle.opacity = 0.6;
82
+ break;
83
+ }
84
+ break;
85
+ case "outline":
86
+ baseStyle.background = "transparent";
87
+ switch (buttonState) {
88
+ case "default":
89
+ baseStyle.border =
90
+ "var(--button-default, 1px) solid var(--action-tertiary-default, #3B3B3B)";
91
+ break;
92
+ case "hover":
93
+ baseStyle.border =
94
+ "var(--button-hover, 2px) solid var(--action-tertiary-hover, #111)";
95
+ break;
96
+ case "active":
97
+ baseStyle.border =
98
+ "var(--button-pressed, 2px) solid var(--action-tertiary-active, #ABABAB)";
99
+ break;
100
+ case "disabled":
101
+ baseStyle.border =
102
+ "var(--button-default, 1px) solid var(--action-tertiary-disabled, #3B3B3B)";
103
+ baseStyle.opacity = 0.6;
104
+ break;
105
+ }
106
+ break;
107
+ case "ghost":
108
+ baseStyle.background = "transparent";
109
+ break;
110
+ case "destructive":
111
+ baseStyle.background = "transparent";
112
+ switch (buttonState) {
113
+ case "default":
114
+ baseStyle.color = "var(--action-destructive-hover, #FF3B30)";
115
+ break;
116
+ case "hover":
117
+ baseStyle.color = "var(--action-destructive-default, #D60000)";
118
+ break;
119
+ case "active":
120
+ baseStyle.color = "var(--action-destructive-active, #FF6259)";
121
+ break;
122
+ case "disabled":
123
+ baseStyle.color = "var(--action-destructive-disabled, #D60000)";
124
+ baseStyle.opacity = 0.6;
125
+ break;
126
+ }
127
+ break;
59
128
  }
60
- }, [variant, size, disabled]);
61
-
62
- // Calculate size styles
63
- const sizeStyles = React.useMemo(() => {
64
- try {
65
- const sizeConfig = buttonTokens.sizes[size];
66
- return {
67
- paddingHorizontal: sizeConfig.paddingHorizontal,
68
- paddingVertical: sizeConfig.paddingVertical,
69
- minHeight: sizeConfig.minHeight,
70
- };
71
- } catch (error) {
72
- // Fallback styles
73
- return {
74
- paddingHorizontal: 16,
75
- paddingVertical: 8,
76
- minHeight: size === "sm" ? 32 : size === "lg" ? 48 : 40,
77
- };
129
+
130
+ return { ...baseStyle, ...style };
131
+ };
132
+
133
+ const getTextStyles = (): React.CSSProperties => {
134
+ const textStyle: React.CSSProperties = {
135
+ fontFamily:
136
+ 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
137
+ fontWeight: 500,
138
+ fontSize: size === "large" ? "1rem" : "0.875rem",
139
+ transition: "all 0.2s ease-in-out",
140
+ };
141
+
142
+ switch (type) {
143
+ case "primary":
144
+ textStyle.color = "var(--neutral-80, #3B3B3B)";
145
+ break;
146
+ case "outline":
147
+ switch (buttonState) {
148
+ case "default":
149
+ textStyle.color = "var(--action-tertiary-default, #3B3B3B)";
150
+ break;
151
+ case "hover":
152
+ textStyle.color = "var(--action-tertiary-hover, #111)";
153
+ break;
154
+ case "active":
155
+ textStyle.color = "var(--action-tertiary-active, #ABABAB)";
156
+ break;
157
+ case "disabled":
158
+ textStyle.color = "var(--action-tertiary-disabled, #3B3B3B)";
159
+ break;
160
+ }
161
+ break;
162
+ case "ghost":
163
+ switch (buttonState) {
164
+ case "default":
165
+ textStyle.color = "var(--action-tertiary-default, #3B3B3B)";
166
+ break;
167
+ case "hover":
168
+ textStyle.color = "var(--action-tertiary-hover, #111)";
169
+ break;
170
+ case "active":
171
+ textStyle.color = "var(--action-tertiary-active, #ABABAB)";
172
+ break;
173
+ case "disabled":
174
+ textStyle.color = "var(--action-tertiary-disabled, #3B3B3B)";
175
+ break;
176
+ }
177
+ break;
178
+ case "destructive":
179
+ break;
78
180
  }
79
- }, [size]);
80
-
81
- // Calculate text styles
82
- const textStyles = React.useMemo(() => {
83
- try {
84
- const sizeConfig = buttonTokens.sizes[size];
85
- const variantConfig = buttonTokens[variant];
86
-
87
- const baseTextStyle: TextStyle = {
88
- fontSize: sizeConfig.fontSize,
89
- fontWeight: buttonTokens.common.fontWeight as any,
90
- lineHeight: buttonTokens.common.lineHeight,
91
- color: disabled
92
- ? variantConfig.text.disabled
93
- : variantConfig.text.default,
94
- };
95
-
96
- return {
97
- ...baseTextStyle,
98
- ...textStyle,
99
- };
100
- } catch (error) {
101
- // Fallback styles
102
- return {
103
- fontSize: size === "sm" ? 12 : size === "lg" ? 18 : 16,
104
- fontWeight: "500",
105
- color: disabled
106
- ? "#8E8E93"
107
- : variant === "primary"
108
- ? "#FFFFFF"
109
- : "#007AFF",
110
- ...textStyle,
111
- };
181
+
182
+ return textStyle;
183
+ };
184
+
185
+ // Icon styles
186
+ const getIconStyles = (): React.CSSProperties => {
187
+ return {
188
+ display: "flex",
189
+ alignItems: "center",
190
+ justifyContent: "center",
191
+ color: "currentColor",
192
+ };
193
+ };
194
+
195
+ // Event handlers for state changes
196
+ const handleMouseEnter = (e: React.MouseEvent<HTMLButtonElement>) => {
197
+ if (!disabled) {
198
+ setButtonState("hover");
199
+ onMouseEnter && onMouseEnter(e);
112
200
  }
113
- }, [variant, size, disabled, textStyle]);
201
+ };
114
202
 
115
- // Handle button press
116
- const handlePress = React.useCallback(() => {
117
- if (!disabled && !loading && onPress) {
118
- onPress();
203
+ const handleMouseLeave = (e: React.MouseEvent<HTMLButtonElement>) => {
204
+ if (!disabled) {
205
+ setButtonState("default");
206
+ onMouseLeave && onMouseLeave(e);
119
207
  }
120
- }, [disabled, loading, onPress]);
121
-
122
- // Combine all styles
123
- const buttonStyles = React.useMemo(
124
- () => ({
125
- ...variantStyles,
126
- ...sizeStyles,
127
- ...(fullWidth && { width: "100%" }),
128
- ...(disabled && { opacity: 0.6 }),
129
- ...style,
130
- }),
131
- [variantStyles, sizeStyles, fullWidth, disabled, style]
132
- );
208
+ };
209
+
210
+ const handleMouseDown = (e: React.MouseEvent<HTMLButtonElement>) => {
211
+ if (!disabled) {
212
+ setButtonState("active");
213
+ onMouseDown && onMouseDown(e);
214
+ }
215
+ };
216
+
217
+ const handleMouseUp = (e: React.MouseEvent<HTMLButtonElement>) => {
218
+ if (!disabled) {
219
+ setButtonState("hover");
220
+ onMouseUp && onMouseUp(e);
221
+ }
222
+ };
133
223
 
134
224
  return (
135
- <TouchableOpacity
136
- style={buttonStyles}
137
- onPress={handlePress}
138
- disabled={disabled || loading}
139
- accessibilityLabel={accessibilityLabel}
140
- accessibilityRole="button"
141
- testID={testID}
225
+ <button
226
+ style={getBaseStyles()}
227
+ onClick={onClick}
228
+ disabled={disabled}
229
+ aria-label={accessibilityLabel || label}
230
+ data-testid={testID}
231
+ className={className}
232
+ onMouseEnter={handleMouseEnter}
233
+ onMouseLeave={handleMouseLeave}
234
+ onMouseDown={handleMouseDown}
235
+ onMouseUp={handleMouseUp}
142
236
  >
143
- {loading && (
144
- <ActivityIndicator
145
- size="small"
146
- color={
147
- typeof textStyles.color === "string" ? textStyles.color : "#000000"
148
- }
149
- style={{ marginRight: 8 }}
150
- />
237
+ {!isIconOnly && leftIcon && (
238
+ <span style={getIconStyles()} className="button-left-icon">
239
+ {leftIconComponent}
240
+ </span>
241
+ )}
242
+
243
+ {!isIconOnly && <span style={getTextStyles()}>{label}</span>}
244
+
245
+ {isIconOnly && <span style={getIconStyles()}>{iconButton}</span>}
246
+
247
+ {!isIconOnly && rightIcon && (
248
+ <span style={getIconStyles()} className="button-right-icon">
249
+ {rightIconComponent}
250
+ </span>
151
251
  )}
152
- <Text style={textStyles}>{children}</Text>
153
- </TouchableOpacity>
252
+ </button>
154
253
  );
155
254
  };
@@ -1,2 +1,7 @@
1
- export { Button } from './Button';
2
- export type { ButtonProps } from '../../types/Button';
1
+ export { Button } from "./Button";
2
+ export type {
3
+ ButtonProps,
4
+ ButtonType,
5
+ ButtonSize,
6
+ ButtonState,
7
+ } from "../../types/Button";
@@ -0,0 +1,113 @@
1
+ import React from 'react';
2
+ import iconData from './icons.json';
3
+
4
+ export interface IconProps {
5
+ /**
6
+ * Name of the icon to display
7
+ */
8
+ name: string;
9
+
10
+ /**
11
+ * Size of the icon in pixels
12
+ */
13
+ size?: number;
14
+
15
+ /**
16
+ * Color of the icon
17
+ */
18
+ color?: string;
19
+
20
+ /**
21
+ * Optional CSS class name
22
+ */
23
+ className?: string;
24
+
25
+ /**
26
+ * Optional style overrides
27
+ */
28
+ style?: React.CSSProperties;
29
+
30
+ /**
31
+ * Optional click handler
32
+ */
33
+ onClick?: React.MouseEventHandler<SVGSVGElement>;
34
+
35
+ /**
36
+ * Aria label for accessibility
37
+ */
38
+ ariaLabel?: string;
39
+ }
40
+
41
+ export interface IconData {
42
+ name: string;
43
+ path: string;
44
+ viewBox: string;
45
+ fill?: string;
46
+ }
47
+
48
+ // Export icon data for use in other components
49
+ export const icons: IconData[] = iconData.icons;
50
+
51
+ // Create a map of icon names to icon data for quick lookup
52
+ const iconMap = new Map<string, IconData>();
53
+ icons.forEach((icon) => {
54
+ iconMap.set(icon.name, icon);
55
+ });
56
+
57
+ export const Icon: React.FC<IconProps> = ({
58
+ name,
59
+ size = 24,
60
+ color = 'currentColor',
61
+ className = '',
62
+ style = {},
63
+ onClick,
64
+ ariaLabel,
65
+ }) => {
66
+ // Find the icon data for the given name
67
+ const iconInfo = iconMap.get(name);
68
+
69
+ // If the icon doesn't exist, return null or a fallback
70
+ if (!iconInfo) {
71
+ console.warn(`Icon "${name}" not found`);
72
+ return null;
73
+ }
74
+
75
+ // Split the path string into multiple paths if it contains multiple commands
76
+ const paths = iconInfo.path.split(' M ').map((path, index) =>
77
+ index === 0 ? path : `M ${path}`
78
+ );
79
+
80
+ // Combine the style props
81
+ const combinedStyle: React.CSSProperties = {
82
+ display: 'inline-block',
83
+ verticalAlign: 'middle',
84
+ ...style,
85
+ };
86
+
87
+ return (
88
+ <svg
89
+ width={size}
90
+ height={size}
91
+ viewBox={iconInfo.viewBox}
92
+ fill="none"
93
+ xmlns="http://www.w3.org/2000/svg"
94
+ className={className}
95
+ style={combinedStyle}
96
+ onClick={onClick}
97
+ aria-label={ariaLabel || `${name} icon`}
98
+ role="img"
99
+ >
100
+ {paths.map((path, index) => (
101
+ <path
102
+ key={index}
103
+ d={path}
104
+ stroke={iconInfo.fill ? 'none' : color}
105
+ fill={iconInfo.fill ? color : 'none'}
106
+ strokeWidth="1.5"
107
+ strokeLinecap="round"
108
+ strokeLinejoin="round"
109
+ />
110
+ ))}
111
+ </svg>
112
+ );
113
+ };
@@ -0,0 +1,110 @@
1
+ import React from 'react';
2
+ import { Icon, icons, IconData } from './Icon';
3
+
4
+ export interface IconListProps {
5
+ /**
6
+ * Search query to filter icons
7
+ */
8
+ searchQuery?: string;
9
+
10
+ /**
11
+ * Size of each icon
12
+ */
13
+ iconSize?: number;
14
+
15
+ /**
16
+ * Color of each icon
17
+ */
18
+ iconColor?: string;
19
+
20
+ /**
21
+ * Function to handle when an icon is clicked
22
+ */
23
+ onIconClick?: (iconName: string) => void;
24
+ }
25
+
26
+ export const IconList: React.FC<IconListProps> = ({
27
+ searchQuery = '',
28
+ iconSize = 24,
29
+ iconColor = 'currentColor',
30
+ onIconClick,
31
+ }) => {
32
+ // Filter icons based on the search query
33
+ const filteredIcons = searchQuery
34
+ ? icons.filter(icon =>
35
+ icon.name.toLowerCase().includes(searchQuery.toLowerCase())
36
+ )
37
+ : icons;
38
+
39
+ const handleIconClick = (iconName: string) => {
40
+ if (onIconClick) {
41
+ onIconClick(iconName);
42
+ }
43
+ };
44
+
45
+ return (
46
+ <div>
47
+ {filteredIcons.length === 0 ? (
48
+ <div style={{ padding: '20px', textAlign: 'center' }}>
49
+ No icons found matching "{searchQuery}"
50
+ </div>
51
+ ) : (
52
+ <div
53
+ style={{
54
+ display: 'grid',
55
+ gridTemplateColumns: 'repeat(auto-fill, minmax(100px, 1fr))',
56
+ gap: '20px',
57
+ }}
58
+ >
59
+ {filteredIcons.map((icon: IconData) => (
60
+ <div
61
+ key={icon.name}
62
+ style={{
63
+ display: 'flex',
64
+ flexDirection: 'column',
65
+ alignItems: 'center',
66
+ padding: '12px',
67
+ border: '1px solid #eee',
68
+ borderRadius: '8px',
69
+ cursor: onIconClick ? 'pointer' : 'default',
70
+ transition: 'all 0.2s ease-in-out',
71
+ }}
72
+ onClick={() => handleIconClick(icon.name)}
73
+ onMouseOver={(e) => {
74
+ e.currentTarget.style.backgroundColor = '#f9f9f9';
75
+ e.currentTarget.style.transform = 'translateY(-2px)';
76
+ e.currentTarget.style.boxShadow = '0 2px 8px rgba(0,0,0,0.05)';
77
+ }}
78
+ onMouseOut={(e) => {
79
+ e.currentTarget.style.backgroundColor = 'transparent';
80
+ e.currentTarget.style.transform = 'translateY(0)';
81
+ e.currentTarget.style.boxShadow = 'none';
82
+ }}
83
+ >
84
+ <div
85
+ style={{
86
+ display: 'flex',
87
+ alignItems: 'center',
88
+ justifyContent: 'center',
89
+ height: '50px',
90
+ marginBottom: '8px',
91
+ }}
92
+ >
93
+ <Icon name={icon.name} size={iconSize} color={iconColor} />
94
+ </div>
95
+ <div
96
+ style={{
97
+ fontSize: '12px',
98
+ textAlign: 'center',
99
+ wordBreak: 'break-word',
100
+ }}
101
+ >
102
+ {icon.name}
103
+ </div>
104
+ </div>
105
+ ))}
106
+ </div>
107
+ )}
108
+ </div>
109
+ );
110
+ };