@idealyst/components 1.1.2 → 1.1.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@idealyst/components",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "description": "Shared component library for React and React Native",
5
5
  "documentation": "https://github.com/IdealystIO/idealyst-framework/tree/main/packages/components#readme",
6
6
  "readme": "README.md",
@@ -25,6 +25,11 @@
25
25
  "require": "./src/index.ts",
26
26
  "types": "./src/index.ts"
27
27
  },
28
+ "./utils": {
29
+ "import": "./src/utils/index.ts",
30
+ "require": "./src/utils/index.ts",
31
+ "types": "./src/utils/index.ts"
32
+ },
28
33
  "./plugin/web": {
29
34
  "import": "./plugin/web.js",
30
35
  "require": "./plugin/web.js",
@@ -41,7 +46,7 @@
41
46
  "publish:npm": "npm publish"
42
47
  },
43
48
  "peerDependencies": {
44
- "@idealyst/theme": "^1.1.2",
49
+ "@idealyst/theme": "^1.1.3",
45
50
  "@mdi/js": ">=7.0.0",
46
51
  "@mdi/react": ">=1.0.0",
47
52
  "@react-native-vector-icons/common": ">=12.0.0",
@@ -91,7 +96,7 @@
91
96
  }
92
97
  },
93
98
  "devDependencies": {
94
- "@idealyst/theme": "^1.1.2",
99
+ "@idealyst/theme": "^1.1.3",
95
100
  "@mdi/react": "^1.6.1",
96
101
  "@types/react": "^19.1.0",
97
102
  "react": "^19.1.0",
@@ -35,6 +35,7 @@ function createTypeVariants(theme: Theme) {
35
35
  borderWidth: 0,
36
36
  },
37
37
  outlined: {
38
+ boxSizing: 'border-box',
38
39
  borderWidth: 1,
39
40
  borderStyle: 'solid' ,
40
41
  backgroundColor: theme.colors.surface.primary,
@@ -121,6 +121,7 @@ export const inputStyles = StyleSheet.create((theme: Theme) => {
121
121
  flexDirection: 'row',
122
122
  alignItems: 'center',
123
123
  width: '100%',
124
+ minWidth: 0,
124
125
  borderRadius: 8,
125
126
  variants: {
126
127
  size: buildSizeVariants(theme, 'input', (size) => ({
@@ -210,7 +210,7 @@ const Slider = forwardRef<View, SliderProps>(({
210
210
  { left: markPosition },
211
211
  ]}
212
212
  >
213
- <Text size="sm">{mark.label}</Text>
213
+ <Text typography="caption">{mark.label}</Text>
214
214
  </View>
215
215
  )}
216
216
  </View>
@@ -243,8 +243,8 @@ const Slider = forwardRef<View, SliderProps>(({
243
243
 
244
244
  {showMinMax && (
245
245
  <View style={sliderStyles.minMaxLabels}>
246
- <Text style={sliderStyles.minMaxLabel} size="sm">{min}</Text>
247
- <Text style={sliderStyles.minMaxLabel} size="sm">{max}</Text>
246
+ <Text style={sliderStyles.minMaxLabel} typography="caption">{min}</Text>
247
+ <Text style={sliderStyles.minMaxLabel} typography="caption">{max}</Text>
248
248
  </View>
249
249
  )}
250
250
  </View>
@@ -1,22 +1,47 @@
1
- import React, { useState, useRef, useEffect, forwardRef } from 'react';
1
+ import React, { useState, useRef, useEffect, forwardRef, ReactNode } from 'react';
2
2
  import { View, TouchableOpacity, Text, ScrollView } from 'react-native';
3
3
  import Animated, { useSharedValue, useAnimatedStyle, withSpring } from 'react-native-reanimated';
4
4
  import {
5
5
  tabBarContainerStyles,
6
6
  tabBarTabStyles,
7
7
  tabBarLabelStyles,
8
- tabBarIndicatorStyles
8
+ tabBarIndicatorStyles,
9
+ tabBarIconStyles
9
10
  } from './TabBar.styles';
10
- import type { TabBarProps } from './types';
11
+ import type { TabBarProps, TabBarItem } from './types';
12
+
13
+ // Icon size mapping based on size variant
14
+ const ICON_SIZES: Record<string, number> = {
15
+ xs: 14,
16
+ sm: 16,
17
+ md: 18,
18
+ lg: 20,
19
+ xl: 24,
20
+ };
21
+
22
+ // Helper to render icon
23
+ function renderIcon(
24
+ icon: TabBarItem['icon'],
25
+ active: boolean,
26
+ size: number
27
+ ): ReactNode {
28
+ if (!icon) return null;
29
+ if (typeof icon === 'function') {
30
+ return icon({ active, size });
31
+ }
32
+ return icon;
33
+ }
11
34
 
12
35
  const TabBar = forwardRef<View, TabBarProps>(({
13
36
  items,
14
37
  value: controlledValue,
15
38
  defaultValue,
16
39
  onChange,
17
- type = 'default',
40
+ type = 'standard',
18
41
  size = 'md',
19
42
  pillMode = 'light',
43
+ iconPosition = 'left',
44
+ justify = 'start',
20
45
  // Spacing variants from ContainerStyleProps
21
46
  gap,
22
47
  padding,
@@ -87,8 +112,10 @@ const TabBar = forwardRef<View, TabBarProps>(({
87
112
 
88
113
  // Apply container and indicator types right before rendering
89
114
  tabBarContainerStyles.useVariants({
115
+ type,
90
116
  size,
91
117
  pillMode,
118
+ justify,
92
119
  gap,
93
120
  padding,
94
121
  paddingVertical,
@@ -97,7 +124,7 @@ const TabBar = forwardRef<View, TabBarProps>(({
97
124
  marginVertical,
98
125
  marginHorizontal,
99
126
  });
100
- tabBarIndicatorStyles.useVariants({ pillMode });
127
+ tabBarIndicatorStyles.useVariants({ type, pillMode });
101
128
 
102
129
  return (
103
130
  <ScrollView
@@ -118,20 +145,33 @@ const TabBar = forwardRef<View, TabBarProps>(({
118
145
  <View style={{ flexDirection: 'row' }}>
119
146
  {items.map((item) => {
120
147
  const isActive = value === item.value;
148
+ const iconSize = ICON_SIZES[size] || 18;
121
149
 
122
150
  // Apply tab and label types for this specific tab
123
151
  tabBarTabStyles.useVariants({
124
152
  size,
153
+ type,
125
154
  active: isActive,
126
155
  disabled: Boolean(item.disabled),
127
156
  pillMode,
157
+ iconPosition,
158
+ justify,
128
159
  });
129
160
  tabBarLabelStyles.useVariants({
130
161
  size,
162
+ type,
131
163
  pillMode,
132
164
  active: isActive,
133
165
  disabled: Boolean(item.disabled),
134
166
  });
167
+ tabBarIconStyles.useVariants({
168
+ size,
169
+ active: isActive,
170
+ disabled: Boolean(item.disabled),
171
+ iconPosition,
172
+ });
173
+
174
+ const icon = renderIcon(item.icon, isActive, iconSize);
135
175
 
136
176
  return (
137
177
  <TouchableOpacity
@@ -146,6 +186,7 @@ const TabBar = forwardRef<View, TabBarProps>(({
146
186
  activeOpacity={0.7}
147
187
  testID={`${testID}-tab-${item.value}`}
148
188
  >
189
+ {icon && <View style={tabBarIconStyles.tabIcon}>{icon}</View>}
149
190
  <Text style={tabBarLabelStyles.tabLabel}>{item.label}</Text>
150
191
  </TouchableOpacity>
151
192
  );
@@ -10,12 +10,13 @@ import {
10
10
  buildMarginVerticalVariants,
11
11
  buildMarginHorizontalVariants,
12
12
  } from '../utils/buildViewStyleVariants';
13
- import { TabBarPillMode, TabBarSizeVariant, TabBarType } from './types';
13
+ import { TabBarPillMode, TabBarSizeVariant, TabBarType, TabBarIconPosition, TabBarJustify } from './types';
14
14
 
15
15
  type TabBarContainerVariants = {
16
16
  type: TabBarType;
17
17
  size: TabBarSizeVariant;
18
18
  pillMode: TabBarPillMode;
19
+ justify: TabBarJustify;
19
20
  }
20
21
 
21
22
  type TabBarTabVariants = {
@@ -24,6 +25,15 @@ type TabBarTabVariants = {
24
25
  active: boolean;
25
26
  disabled: boolean;
26
27
  pillMode: TabBarPillMode;
28
+ iconPosition: TabBarIconPosition;
29
+ justify: TabBarJustify;
30
+ }
31
+
32
+ type TabBarIconVariants = {
33
+ size: TabBarSizeVariant;
34
+ active: boolean;
35
+ disabled: boolean;
36
+ iconPosition: TabBarIconPosition;
27
37
  }
28
38
 
29
39
  type TabBarLabelVariants = {
@@ -238,6 +248,22 @@ const createContainerStyles = (theme: Theme) => {
238
248
  light: {},
239
249
  dark: {},
240
250
  },
251
+ justify: {
252
+ start: {
253
+ justifyContent: 'flex-start',
254
+ },
255
+ center: {
256
+ justifyContent: 'center',
257
+ },
258
+ equal: {
259
+ justifyContent: 'stretch',
260
+ width: '100%',
261
+ },
262
+ 'space-between': {
263
+ justifyContent: 'space-between',
264
+ width: '100%',
265
+ },
266
+ },
241
267
  // Spacing variants from ContainerStyleProps
242
268
  gap: buildGapVariants(theme),
243
269
  padding: buildPaddingVariants(theme),
@@ -262,6 +288,7 @@ const createTabStyles = (theme: Theme) => {
262
288
  position: 'relative',
263
289
  zIndex: 2,
264
290
  backgroundColor: 'transparent',
291
+ gap: 6,
265
292
  variants: {
266
293
  size: createTabSizeVariants(theme),
267
294
  type: {
@@ -298,6 +325,22 @@ const createTabStyles = (theme: Theme) => {
298
325
  light: {},
299
326
  dark: {},
300
327
  },
328
+ iconPosition: {
329
+ left: {
330
+ flexDirection: 'row',
331
+ },
332
+ top: {
333
+ flexDirection: 'column',
334
+ },
335
+ },
336
+ justify: {
337
+ start: {},
338
+ center: {},
339
+ equal: {
340
+ flex: 1,
341
+ },
342
+ 'space-between': {},
343
+ },
301
344
  } as const,
302
345
  compoundVariants: createTabCompoundVariants(theme),
303
346
  _web: {
@@ -379,6 +422,43 @@ const createIndicatorStyles = (theme: Theme) => {
379
422
  } as const;
380
423
  }
381
424
 
425
+ /**
426
+ * Create size variants for icon
427
+ */
428
+ function createIconSizeVariants(theme: Theme) {
429
+ return buildSizeVariants(theme, 'tabBar', (size) => ({
430
+ width: size.iconSize || size.fontSize,
431
+ height: size.iconSize || size.fontSize,
432
+ }));
433
+ }
434
+
435
+ const createIconStyles = (theme: Theme) => {
436
+ return {
437
+ display: 'flex',
438
+ alignItems: 'center',
439
+ justifyContent: 'center',
440
+ variants: {
441
+ size: createIconSizeVariants(theme),
442
+ active: {
443
+ true: {},
444
+ false: {},
445
+ },
446
+ disabled: {
447
+ true: {
448
+ opacity: 0.5,
449
+ },
450
+ false: {},
451
+ },
452
+ iconPosition: {
453
+ left: {},
454
+ top: {
455
+ marginBottom: 2,
456
+ },
457
+ },
458
+ } as const,
459
+ } as const;
460
+ }
461
+
382
462
  // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel transform on native cannot resolve function calls to extract variant structures.
383
463
  // @ts-ignore - TS language server needs restart to pick up theme structure changes
384
464
  export const tabBarStyles = StyleSheet.create((theme: Theme) => {
@@ -386,6 +466,7 @@ export const tabBarStyles = StyleSheet.create((theme: Theme) => {
386
466
  container: createContainerStyles(theme),
387
467
  tab: createTabStyles(theme),
388
468
  tabLabel: createTabLabelStyles(theme),
469
+ tabIcon: createIconStyles(theme),
389
470
  indicator: createIndicatorStyles(theme),
390
471
  };
391
472
  });
@@ -414,3 +495,9 @@ export const tabBarIndicatorStyles = StyleSheet.create((theme: Theme) => {
414
495
  indicator: createIndicatorStyles(theme),
415
496
  } as const;
416
497
  });
498
+
499
+ export const tabBarIconStyles = StyleSheet.create((theme: Theme) => {
500
+ return {
501
+ tabIcon: createIconStyles(theme),
502
+ } as const;
503
+ });
@@ -1,14 +1,37 @@
1
- import React, { useState, useRef, useEffect } from 'react';
1
+ import React, { useState, useRef, useEffect, ReactNode } from 'react';
2
2
  import { getWebProps } from 'react-native-unistyles/web';
3
3
  import {
4
4
  tabBarContainerStyles,
5
5
  tabBarTabStyles,
6
6
  tabBarLabelStyles,
7
- tabBarIndicatorStyles
7
+ tabBarIndicatorStyles,
8
+ tabBarIconStyles
8
9
  } from './TabBar.styles';
9
10
  import type { TabBarProps, TabBarItem } from './types';
10
11
  import useMergeRefs from '../hooks/useMergeRefs';
11
12
 
13
+ // Icon size mapping based on size variant
14
+ const ICON_SIZES: Record<string, number> = {
15
+ xs: 14,
16
+ sm: 16,
17
+ md: 18,
18
+ lg: 20,
19
+ xl: 24,
20
+ };
21
+
22
+ // Helper to render icon
23
+ function renderIcon(
24
+ icon: TabBarItem['icon'],
25
+ active: boolean,
26
+ size: number
27
+ ): ReactNode {
28
+ if (!icon) return null;
29
+ if (typeof icon === 'function') {
30
+ return icon({ active, size });
31
+ }
32
+ return icon;
33
+ }
34
+
12
35
  interface TabProps {
13
36
  item: TabBarItem;
14
37
  isActive: boolean;
@@ -16,6 +39,8 @@ interface TabProps {
16
39
  size: TabBarProps['size'];
17
40
  type: TabBarProps['type'];
18
41
  pillMode: TabBarProps['pillMode'];
42
+ iconPosition: TabBarProps['iconPosition'];
43
+ justify: TabBarProps['justify'];
19
44
  testID?: string;
20
45
  tabRef: (el: HTMLButtonElement | null) => void;
21
46
  }
@@ -27,9 +52,13 @@ const Tab: React.FC<TabProps> = ({
27
52
  size,
28
53
  type,
29
54
  pillMode,
55
+ iconPosition,
56
+ justify,
30
57
  testID,
31
58
  tabRef,
32
59
  }) => {
60
+ const iconSize = ICON_SIZES[size || 'md'] || 18;
61
+
33
62
  // Apply tab and label types for this specific tab
34
63
  tabBarTabStyles.useVariants({
35
64
  size,
@@ -37,6 +66,8 @@ const Tab: React.FC<TabProps> = ({
37
66
  active: isActive,
38
67
  disabled: Boolean(item.disabled),
39
68
  pillMode,
69
+ iconPosition,
70
+ justify,
40
71
  });
41
72
  tabBarLabelStyles.useVariants({
42
73
  size,
@@ -45,9 +76,16 @@ const Tab: React.FC<TabProps> = ({
45
76
  active: isActive,
46
77
  disabled: Boolean(item.disabled),
47
78
  });
79
+ tabBarIconStyles.useVariants({
80
+ size,
81
+ active: isActive,
82
+ disabled: Boolean(item.disabled),
83
+ iconPosition,
84
+ });
48
85
 
49
86
  const tabProps = getWebProps([tabBarTabStyles.tab]);
50
87
  const labelProps = getWebProps([tabBarLabelStyles.tabLabel]);
88
+ const iconProps = getWebProps([tabBarIconStyles.tabIcon]);
51
89
 
52
90
  // Merge refs from getWebProps with our tracking ref
53
91
  const mergedRef = useMergeRefs<HTMLButtonElement>(
@@ -55,6 +93,8 @@ const Tab: React.FC<TabProps> = ({
55
93
  tabRef
56
94
  );
57
95
 
96
+ const icon = renderIcon(item.icon, isActive, iconSize);
97
+
58
98
  return (
59
99
  <button
60
100
  {...tabProps}
@@ -66,6 +106,7 @@ const Tab: React.FC<TabProps> = ({
66
106
  aria-disabled={item.disabled}
67
107
  data-testid={`${testID}-tab-${item.value}`}
68
108
  >
109
+ {icon && <span {...iconProps}>{icon}</span>}
69
110
  <span {...labelProps}>{item.label}</span>
70
111
  </button>
71
112
  );
@@ -79,6 +120,8 @@ const TabBar: React.FC<TabBarProps> = ({
79
120
  type = 'standard',
80
121
  size = 'md',
81
122
  pillMode = 'light',
123
+ iconPosition = 'left',
124
+ justify = 'start',
82
125
  // Spacing variants from ContainerStyleProps
83
126
  gap,
84
127
  padding,
@@ -152,6 +195,7 @@ const TabBar: React.FC<TabBarProps> = ({
152
195
  type,
153
196
  size,
154
197
  pillMode,
198
+ justify,
155
199
  gap,
156
200
  padding,
157
201
  paddingVertical,
@@ -208,6 +252,8 @@ const TabBar: React.FC<TabBarProps> = ({
208
252
  size={size}
209
253
  type={type}
210
254
  pillMode={pillMode}
255
+ iconPosition={iconPosition}
256
+ justify={justify}
211
257
  testID={testID}
212
258
  tabRef={(el) => {
213
259
  tabRefs.current[item.value] = el;
@@ -1,4 +1,5 @@
1
1
  import { Size } from '@idealyst/theme';
2
+ import type { ReactNode } from 'react';
2
3
  import type { StyleProp, ViewStyle } from 'react-native';
3
4
  import { ContainerStyleProps } from '../utils/viewStyleProps';
4
5
 
@@ -6,10 +7,15 @@ import { ContainerStyleProps } from '../utils/viewStyleProps';
6
7
  export type TabBarSizeVariant = Size;
7
8
  export type TabBarType = 'standard' | 'pills' | 'underline';
8
9
  export type TabBarPillMode = 'light' | 'dark';
10
+ export type TabBarIconPosition = 'left' | 'top';
11
+ /** Layout justification for tabs */
12
+ export type TabBarJustify = 'start' | 'center' | 'equal' | 'space-between';
9
13
 
10
14
  export interface TabBarItem {
11
15
  value: string;
12
16
  label: string;
17
+ /** Icon to display - can be a React node or a render function receiving active state */
18
+ icon?: ReactNode | ((props: { active: boolean; size: number }) => ReactNode);
13
19
  disabled?: boolean;
14
20
  }
15
21
 
@@ -22,6 +28,10 @@ export interface TabBarProps extends ContainerStyleProps {
22
28
  size?: TabBarSizeVariant;
23
29
  /** Mode for pills variant: 'light' for light backgrounds (dark pill), 'dark' for dark backgrounds (light pill) */
24
30
  pillMode?: TabBarPillMode;
31
+ /** Position of icon relative to label: 'left' (horizontal) or 'top' (stacked) */
32
+ iconPosition?: TabBarIconPosition;
33
+ /** Layout justification: 'start' (left), 'center', 'equal' (full width equal tabs), 'space-between' */
34
+ justify?: TabBarJustify;
25
35
  style?: StyleProp<ViewStyle>;
26
36
  testID?: string;
27
37
  }
@@ -5,9 +5,8 @@ import { textStyles } from './Text.styles';
5
5
 
6
6
  const Text = forwardRef<RNText, TextProps>(({
7
7
  children,
8
- typography,
9
- size = 'md',
10
- weight = 'normal',
8
+ typography = 'body1',
9
+ weight,
11
10
  color,
12
11
  align = 'left',
13
12
  // Spacing variants from TextSpacingStyleProps
@@ -19,10 +18,9 @@ const Text = forwardRef<RNText, TextProps>(({
19
18
  testID,
20
19
  id,
21
20
  }, ref) => {
22
- // When typography is set, it overrides size and weight (handled in styles)
23
21
  textStyles.useVariants({
24
- size: typography ? 'md' : size, // Use default when typography is set (will be overridden)
25
- weight: typography ? 'normal' : weight, // Use default when typography is set (will be overridden)
22
+ typography,
23
+ weight,
26
24
  align,
27
25
  gap,
28
26
  padding,
@@ -35,7 +33,7 @@ const Text = forwardRef<RNText, TextProps>(({
35
33
  ref={ref}
36
34
  nativeID={id}
37
35
  style={[
38
- textStyles.text({ color, typography }),
36
+ textStyles.text({ color }),
39
37
  style,
40
38
  ]}
41
39
  testID={testID}
@@ -1,47 +1,51 @@
1
1
  import { StyleSheet } from "react-native-unistyles";
2
- import { Theme } from '@idealyst/theme';
3
- import { buildSizeVariants } from '../utils/buildSizeVariants';
2
+ import { Theme, Typography } from '@idealyst/theme';
4
3
  import {
5
4
  buildGapVariants,
6
5
  buildPaddingVariants,
7
6
  buildPaddingVerticalVariants,
8
7
  buildPaddingHorizontalVariants,
9
8
  } from '../utils/buildViewStyleVariants';
10
- import { TextAlignVariant, TextColorVariant, TextSizeVariant, TextWeightVariant, TextTypographyVariant } from "./types";
9
+ import { TextAlignVariant, TextColorVariant, TextWeightVariant, TextTypographyVariant } from "./types";
11
10
 
12
- type TextVariants = {
13
- size: TextSizeVariant;
11
+ export type TextVariants = {
12
+ typography: TextTypographyVariant;
14
13
  weight: TextWeightVariant;
15
14
  align: TextAlignVariant;
16
15
  }
17
16
 
18
17
  /**
19
- * Create size variants for text (legacy)
20
- * @deprecated Use typography prop instead
18
+ * Create typography variants from theme
21
19
  */
22
- function createSizeVariants(theme: Theme): any {
23
- return buildSizeVariants(theme, 'text', (size) => ({
24
- fontSize: size.fontSize,
25
- lineHeight: size.lineHeight,
26
- }));
20
+ function createTypographyVariants(theme: Theme) {
21
+ const variants: Record<string, object> = {};
22
+
23
+ for (const key in theme.sizes.typography) {
24
+ const typo = theme.sizes.typography[key as Typography];
25
+ variants[key] = {
26
+ fontSize: typo.fontSize,
27
+ lineHeight: typo.lineHeight,
28
+ fontWeight: typo.fontWeight,
29
+ };
30
+ }
31
+
32
+ return variants;
27
33
  }
28
34
 
29
35
  type TextStyleParams = {
30
36
  color?: TextColorVariant;
31
- typography?: TextTypographyVariant;
32
37
  }
33
38
 
34
39
  function createTextStyles(theme: Theme) {
35
- return ({ color, typography }: TextStyleParams) => {
36
- const colorValue = theme.colors.text[color] || theme.colors.text.primary;
40
+ return ({ color }: TextStyleParams) => {
41
+ const colorValue = theme.colors.text[color ?? 'primary'] || theme.colors.text.primary;
37
42
 
38
- // Base styles
39
- const baseStyles: any = {
43
+ return {
40
44
  margin: 0,
41
45
  padding: 0,
42
46
  color: colorValue,
43
47
  variants: {
44
- size: createSizeVariants(theme) as any,
48
+ typography: createTypographyVariants(theme),
45
49
  weight: {
46
50
  light: {
47
51
  fontWeight: '300',
@@ -78,19 +82,8 @@ function createTextStyles(theme: Theme) {
78
82
  } as const,
79
83
  _web: {
80
84
  fontFamily: 'inherit',
81
- lineHeight: 'inherit',
82
85
  },
83
86
  };
84
-
85
- // If typography is set, apply typography styles (overrides size/weight variants)
86
- if (typography && theme.sizes.typography[typography]) {
87
- const typo = theme.sizes.typography[typography];
88
- baseStyles.fontSize = typo.fontSize;
89
- baseStyles.lineHeight = typo.lineHeight;
90
- baseStyles.fontWeight = typo.fontWeight;
91
- }
92
-
93
- return baseStyles;
94
87
  }
95
88
  }
96
89
 
@@ -100,4 +93,4 @@ export const textStyles = StyleSheet.create((theme: Theme) => {
100
93
  return {
101
94
  text: createTextStyles(theme),
102
95
  };
103
- });
96
+ });
@@ -6,9 +6,8 @@ import useMergeRefs from '../hooks/useMergeRefs';
6
6
 
7
7
  const Text = forwardRef<HTMLSpanElement, TextProps>(({
8
8
  children,
9
- typography,
10
- size = 'md',
11
- weight = 'normal',
9
+ typography = 'body1',
10
+ weight,
12
11
  color = 'primary',
13
12
  align = 'left',
14
13
  // Spacing variants from TextSpacingStyleProps
@@ -20,10 +19,9 @@ const Text = forwardRef<HTMLSpanElement, TextProps>(({
20
19
  testID,
21
20
  id,
22
21
  }, ref) => {
23
- // When typography is set, it overrides size and weight (handled in styles)
24
22
  textStyles.useVariants({
25
- size: typography ? 'md' : size, // Use default when typography is set (will be overridden)
26
- weight: typography ? 'normal' : weight, // Use default when typography is set (will be overridden)
23
+ typography,
24
+ weight,
27
25
  align,
28
26
  gap,
29
27
  padding,
@@ -31,9 +29,9 @@ const Text = forwardRef<HTMLSpanElement, TextProps>(({
31
29
  paddingHorizontal,
32
30
  });
33
31
 
34
- // Create the style array - pass typography to dynamic style function
32
+ // Create the style array
35
33
  const textStyleArray = [
36
- textStyles.text({ color, typography }),
34
+ textStyles.text({ color }),
37
35
  style,
38
36
  ];
39
37
 
package/src/Text/types.ts CHANGED
@@ -5,7 +5,6 @@ import { TextSpacingStyleProps } from '../utils/viewStyleProps';
5
5
 
6
6
  // Component-specific type aliases for future extensibility
7
7
  export type TextColorVariant = Text;
8
- export type TextSizeVariant = 'sm' | 'md' | 'lg' | 'xl'; // Using sm/md/lg/xl for consistency
9
8
  export type TextWeightVariant = 'light' | 'normal' | 'medium' | 'semibold' | 'bold';
10
9
  export type TextAlignVariant = 'left' | 'center' | 'right';
11
10
  export type TextTypographyVariant = Typography;
@@ -19,19 +18,12 @@ export interface TextProps extends TextSpacingStyleProps {
19
18
  /**
20
19
  * Typography variant for semantic text styling.
21
20
  * Automatically sets fontSize, lineHeight, and fontWeight.
22
- * When set, overrides size and weight props.
23
21
  */
24
22
  typography?: TextTypographyVariant;
25
23
 
26
- /**
27
- * The size variant of the text
28
- * @deprecated Use `typography` prop instead for semantic text styling (e.g., 'h1', 'body1', 'caption')
29
- */
30
- size?: TextSizeVariant;
31
-
32
24
  /**
33
25
  * The weight of the text.
34
- * Note: When `typography` is set, this is ignored as typography includes fontWeight.
26
+ * Overrides the weight from typography if both are set.
35
27
  */
36
28
  weight?: TextWeightVariant;
37
29
 
@@ -1,5 +1,5 @@
1
1
  import React, { useState } from 'react';
2
- import { Screen, View, Text } from '@idealyst/components';
2
+ import { Screen, View, Text, Icon } from '@idealyst/components';
3
3
  import TabBar from '../TabBar';
4
4
  import type { TabBarItem } from '../TabBar/types';
5
5
 
@@ -7,6 +7,8 @@ export const TabBarExamples: React.FC = () => {
7
7
  const [activeTab, setActiveTab] = useState('tab1');
8
8
  const [variantTab, setVariantTab] = useState('home');
9
9
  const [pillTab, setPillTab] = useState('tab1');
10
+ const [iconTab, setIconTab] = useState('home');
11
+ const [justifyTab, setJustifyTab] = useState('tab1');
10
12
 
11
13
  const basicTabs: TabBarItem[] = [
12
14
  { value: 'tab1', label: 'Tab 1' },
@@ -26,6 +28,38 @@ export const TabBarExamples: React.FC = () => {
26
28
  { value: 'tab3', label: 'Enabled' },
27
29
  ];
28
30
 
31
+ // Tabs with icons using render function
32
+ const iconTabs: TabBarItem[] = [
33
+ {
34
+ value: 'home',
35
+ label: 'Home',
36
+ icon: ({ active, size }) => (
37
+ <Icon name="home" size={size} color={active ? 'primary' : 'secondary'} />
38
+ ),
39
+ },
40
+ {
41
+ value: 'search',
42
+ label: 'Search',
43
+ icon: ({ active, size }) => (
44
+ <Icon name="magnify" size={size} color={active ? 'primary' : 'secondary'} />
45
+ ),
46
+ },
47
+ {
48
+ value: 'profile',
49
+ label: 'Profile',
50
+ icon: ({ active, size }) => (
51
+ <Icon name="account" size={size} color={active ? 'primary' : 'secondary'} />
52
+ ),
53
+ },
54
+ {
55
+ value: 'settings',
56
+ label: 'Settings',
57
+ icon: ({ active, size }) => (
58
+ <Icon name="cog" size={size} color={active ? 'primary' : 'secondary'} />
59
+ ),
60
+ },
61
+ ];
62
+
29
63
  return (
30
64
  <Screen background="primary" padding="lg">
31
65
  <View gap="lg">
@@ -135,6 +169,93 @@ export const TabBarExamples: React.FC = () => {
135
169
  onChange={setActiveTab}
136
170
  />
137
171
  </View>
172
+
173
+ <View gap="md">
174
+ <Text typography="h5">With Icons</Text>
175
+ <View gap="sm">
176
+ <View gap="xs">
177
+ <Text typography="body2">Icons Left (default)</Text>
178
+ <TabBar
179
+ items={iconTabs}
180
+ value={iconTab}
181
+ onChange={setIconTab}
182
+ iconPosition="left"
183
+ />
184
+ </View>
185
+ <View gap="xs">
186
+ <Text typography="body2">Icons Top (stacked)</Text>
187
+ <TabBar
188
+ items={iconTabs}
189
+ value={iconTab}
190
+ onChange={setIconTab}
191
+ iconPosition="top"
192
+ />
193
+ </View>
194
+ <View gap="xs">
195
+ <Text typography="body2">Pills with Icons</Text>
196
+ <TabBar
197
+ items={iconTabs}
198
+ value={iconTab}
199
+ onChange={setIconTab}
200
+ type="pills"
201
+ iconPosition="left"
202
+ />
203
+ </View>
204
+ </View>
205
+ </View>
206
+
207
+ <View gap="md">
208
+ <Text typography="h5">Layout Justification</Text>
209
+ <View gap="sm">
210
+ <View gap="xs">
211
+ <Text typography="body2">Start (default)</Text>
212
+ <TabBar
213
+ items={basicTabs}
214
+ value={justifyTab}
215
+ onChange={setJustifyTab}
216
+ justify="start"
217
+ />
218
+ </View>
219
+ <View gap="xs">
220
+ <Text typography="body2">Center</Text>
221
+ <TabBar
222
+ items={basicTabs}
223
+ value={justifyTab}
224
+ onChange={setJustifyTab}
225
+ justify="center"
226
+ />
227
+ </View>
228
+ <View gap="xs">
229
+ <Text typography="body2">Equal (full width, equal tabs)</Text>
230
+ <TabBar
231
+ items={basicTabs}
232
+ value={justifyTab}
233
+ onChange={setJustifyTab}
234
+ justify="equal"
235
+ />
236
+ </View>
237
+ <View gap="xs">
238
+ <Text typography="body2">Space Between</Text>
239
+ <TabBar
240
+ items={basicTabs}
241
+ value={justifyTab}
242
+ onChange={setJustifyTab}
243
+ justify="space-between"
244
+ />
245
+ </View>
246
+ </View>
247
+ </View>
248
+
249
+ <View gap="md">
250
+ <Text typography="h5">Full Width with Icons</Text>
251
+ <TabBar
252
+ items={iconTabs}
253
+ value={iconTab}
254
+ onChange={setIconTab}
255
+ justify="equal"
256
+ iconPosition="top"
257
+ />
258
+ </View>
138
259
  </View>
139
260
  </Screen>
140
261
  );
@@ -0,0 +1,20 @@
1
+ // Size variant builder
2
+ export { buildSizeVariants } from './buildSizeVariants';
3
+
4
+ // View/container style variant builders
5
+ export {
6
+ buildGapVariants,
7
+ buildPaddingVariants,
8
+ buildPaddingVerticalVariants,
9
+ buildPaddingHorizontalVariants,
10
+ buildMarginVariants,
11
+ buildMarginVerticalVariants,
12
+ buildMarginHorizontalVariants,
13
+ buildContainerStyleVariants,
14
+ buildPaddingStyleVariants,
15
+ buildMarginStyleVariants,
16
+ buildTextSpacingVariants,
17
+ } from './buildViewStyleVariants';
18
+
19
+ // General style helpers
20
+ export { deepMerge, isPlainObject } from './styleHelpers';