@vellira-ui/react-native 2.26.1 → 2.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +2 -2
  2. package/dist/index.js +152 -52
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -54,7 +54,7 @@ export function Example() {
54
54
  checked={accepted}
55
55
  onCheckedChange={setAccepted}
56
56
  />
57
- <Button color='primary' variant='solid'>
57
+ <Button color='primary' appearance='solid'>
58
58
  Continue
59
59
  </Button>
60
60
  </View>
@@ -69,7 +69,7 @@ Use `accessibilityLabel` for icon-only native buttons:
69
69
  ```tsx
70
70
  import { Search } from '@vellira-ui/icons';
71
71
 
72
- <Button accessibilityLabel='Search' iconOnly leftIcon={<Search />} />;
72
+ <Button accessibilityLabel='Search' iconOnly iconStart={<Search />} />;
73
73
  ```
74
74
 
75
75
  Button icons are React elements from `@vellira-ui/icons`; the component injects
package/dist/index.js CHANGED
@@ -2207,7 +2207,7 @@ Tooltip.displayName = "Tooltip";
2207
2207
 
2208
2208
  // src/primitives/Button/Button.tsx
2209
2209
  import { cloneElement as cloneElement5, useState as useState5 } from "react";
2210
- import { ActivityIndicator, Pressable as Pressable10, Text as Text11 } from "react-native";
2210
+ import { ActivityIndicator, Pressable as Pressable10, Text as Text11, View as View19 } from "react-native";
2211
2211
 
2212
2212
  // src/utils/devWarning.ts
2213
2213
  var devWarning = (condition, message) => {
@@ -2225,7 +2225,6 @@ var createStyles21 = (theme) => StyleSheet21.create({
2225
2225
  alignItems: "center",
2226
2226
  justifyContent: "center",
2227
2227
  gap: 8,
2228
- borderRadius: theme.tokens.radius.md,
2229
2228
  borderWidth: 1
2230
2229
  },
2231
2230
  fullWidth: {
@@ -2239,10 +2238,32 @@ var createStyles21 = (theme) => StyleSheet21.create({
2239
2238
  // Fallback color.
2240
2239
  color: theme.components.button.primary.solid.default.fg
2241
2240
  },
2241
+ labelSlot: {
2242
+ position: "relative"
2243
+ },
2244
+ labelMeasure: {
2245
+ position: "absolute",
2246
+ opacity: 0
2247
+ },
2242
2248
  spinner: {
2243
2249
  fontSize: 12,
2244
2250
  lineHeight: theme.tokens.typography.lineHeight.md
2245
2251
  },
2252
+ badge: {
2253
+ minWidth: 18,
2254
+ height: 18,
2255
+ paddingHorizontal: 6,
2256
+ borderRadius: theme.tokens.radius.full,
2257
+ overflow: "hidden",
2258
+ textAlign: "center",
2259
+ fontSize: 11,
2260
+ lineHeight: 18,
2261
+ fontWeight: fontWeight3(theme.tokens.typography.weight.medium)
2262
+ },
2263
+ shortcut: {
2264
+ opacity: 0.8,
2265
+ fontSize: 12
2266
+ },
2246
2267
  disabled: {
2247
2268
  borderColor: theme.components.button.disabled.border
2248
2269
  },
@@ -2290,7 +2311,8 @@ var sizeMap = {
2290
2311
  function Button({
2291
2312
  children,
2292
2313
  color = "primary",
2293
- variant = "solid",
2314
+ appearance = "solid",
2315
+ shape = "pill",
2294
2316
  disabled = false,
2295
2317
  loading = false,
2296
2318
  loadingText,
@@ -2300,8 +2322,10 @@ function Button({
2300
2322
  onHoverIn,
2301
2323
  onHoverOut,
2302
2324
  size = "md",
2303
- leftIcon,
2304
- rightIcon,
2325
+ iconStart,
2326
+ iconEnd,
2327
+ badge,
2328
+ shortcut,
2305
2329
  fullWidth = false,
2306
2330
  iconOnly: iconOnlyProp = false,
2307
2331
  style,
@@ -2314,12 +2338,15 @@ function Button({
2314
2338
  const { theme } = useTheme();
2315
2339
  const styles = useThemeStyles(createStyles21);
2316
2340
  const config = sizeMap[size];
2317
- const variantTheme = theme.components.button[color][variant];
2341
+ const radius = shape === "square" ? theme.tokens.radius.sm : shape === "rounded" ? theme.tokens.radius.md : theme.tokens.radius.full;
2342
+ const appearanceTheme = theme.components.button[color][appearance];
2318
2343
  const [isHovered, setIsHovered] = useState5(false);
2319
2344
  const [isFocused, setIsFocused] = useState5(false);
2345
+ const [labelWidth, setLabelWidth] = useState5(0);
2320
2346
  const isDisabled = disabled || loading;
2321
- const iconOnly = iconOnlyProp || !children && Boolean(leftIcon || rightIcon);
2347
+ const iconOnly = iconOnlyProp || !children && Boolean(iconStart || iconEnd);
2322
2348
  const content = loading && loadingText ? loadingText : children;
2349
+ const measureLabel = loadingText && children && !iconOnly ? loading ? children : loadingText : void 0;
2323
2350
  devWarning(
2324
2351
  !iconOnly || Boolean(accessibilityLabel),
2325
2352
  "Button: icon-only buttons must provide an accessibilityLabel."
@@ -2345,7 +2372,13 @@ function Button({
2345
2372
  color: iconColor,
2346
2373
  size: resolvedIconSize
2347
2374
  });
2348
- const getInteractionTheme = (pressed) => isDisabled ? theme.components.button.disabled : pressed ? variantTheme.pressed : isHovered ? variantTheme.hover : variantTheme.default;
2375
+ const handleLabelLayout = (event) => {
2376
+ const width = event.nativeEvent.layout.width;
2377
+ setLabelWidth(
2378
+ (currentWidth) => width > currentWidth ? width : currentWidth
2379
+ );
2380
+ };
2381
+ const getInteractionTheme = (pressed) => isDisabled ? theme.components.button.disabled : pressed ? appearanceTheme.pressed : isHovered ? appearanceTheme.hover : appearanceTheme.default;
2349
2382
  return /* @__PURE__ */ jsx23(
2350
2383
  Pressable10,
2351
2384
  {
@@ -2367,6 +2400,7 @@ function Button({
2367
2400
  {
2368
2401
  backgroundColor: interactionTheme.bg,
2369
2402
  borderColor: interactionTheme.border,
2403
+ borderRadius: radius,
2370
2404
  paddingHorizontal: iconOnly ? 0 : config.px,
2371
2405
  paddingVertical: iconOnly ? 0 : config.py,
2372
2406
  width: iconOnly ? config.height : void 0,
@@ -2384,22 +2418,68 @@ function Button({
2384
2418
  const contentColor = interactionTheme.fg;
2385
2419
  return /* @__PURE__ */ jsxs12(Fragment3, { children: [
2386
2420
  loading && /* @__PURE__ */ jsx23(ActivityIndicator, { size: "small", color: contentColor }),
2387
- !loading && leftIcon && renderIcon(leftIcon, contentColor),
2388
- content && !iconOnly && /* @__PURE__ */ jsx23(
2421
+ !loading && iconStart && renderIcon(iconStart, contentColor),
2422
+ content && !iconOnly && /* @__PURE__ */ jsxs12(View19, { style: styles.labelSlot, children: [
2423
+ /* @__PURE__ */ jsx23(
2424
+ Text11,
2425
+ {
2426
+ onLayout: handleLabelLayout,
2427
+ style: [
2428
+ styles.text,
2429
+ labelWidth > 0 && { minWidth: labelWidth },
2430
+ {
2431
+ fontSize: config.fontSize,
2432
+ color: contentColor
2433
+ },
2434
+ textStyle
2435
+ ],
2436
+ children: content
2437
+ }
2438
+ ),
2439
+ measureLabel && /* @__PURE__ */ jsx23(
2440
+ Text11,
2441
+ {
2442
+ accessibilityElementsHidden: true,
2443
+ importantForAccessibility: "no-hide-descendants",
2444
+ onLayout: handleLabelLayout,
2445
+ style: [
2446
+ styles.text,
2447
+ styles.labelMeasure,
2448
+ {
2449
+ fontSize: config.fontSize
2450
+ },
2451
+ textStyle
2452
+ ],
2453
+ children: measureLabel
2454
+ }
2455
+ )
2456
+ ] }),
2457
+ badge && !iconOnly && /* @__PURE__ */ jsx23(
2389
2458
  Text11,
2390
2459
  {
2391
2460
  style: [
2392
- styles.text,
2461
+ styles.badge,
2393
2462
  {
2394
- fontSize: config.fontSize,
2463
+ backgroundColor: `${contentColor}24`,
2395
2464
  color: contentColor
2396
- },
2397
- textStyle
2465
+ }
2398
2466
  ],
2399
- children: content
2467
+ children: badge
2400
2468
  }
2401
2469
  ),
2402
- !loading && rightIcon && renderIcon(rightIcon, contentColor)
2470
+ shortcut && !iconOnly && /* @__PURE__ */ jsx23(
2471
+ Text11,
2472
+ {
2473
+ style: [
2474
+ styles.shortcut,
2475
+ {
2476
+ color: contentColor
2477
+ }
2478
+ ],
2479
+ children: shortcut
2480
+ }
2481
+ ),
2482
+ !loading && iconEnd && renderIcon(iconEnd, contentColor)
2403
2483
  ] });
2404
2484
  }
2405
2485
  }
@@ -2410,7 +2490,7 @@ function Button({
2410
2490
  import { forwardRef as forwardRef2, useEffect as useEffect4 } from "react";
2411
2491
  import { useControllableState as useControllableState3 } from "@vellira-ui/core";
2412
2492
  import { Check } from "@vellira-ui/icons";
2413
- import { Pressable as Pressable11, Text as Text12, View as View19 } from "react-native";
2493
+ import { Pressable as Pressable11, Text as Text12, View as View20 } from "react-native";
2414
2494
 
2415
2495
  // src/primitives/Checkbox/Checkbox.styles.ts
2416
2496
  import { StyleSheet as StyleSheet22 } from "react-native";
@@ -2423,6 +2503,9 @@ var createStyles22 = (theme) => StyleSheet22.create({
2423
2503
  alignItems: "center",
2424
2504
  gap: theme.tokens.spacing[3]
2425
2505
  },
2506
+ wrapperLabelStart: {
2507
+ flexDirection: "row-reverse"
2508
+ },
2426
2509
  iconOnly: {
2427
2510
  justifyContent: "center",
2428
2511
  minWidth: 44,
@@ -2456,16 +2539,16 @@ var createStyles22 = (theme) => StyleSheet22.create({
2456
2539
  borderWidth: 2
2457
2540
  },
2458
2541
  boxChecked: {
2459
- backgroundColor: theme.components.checkbox.checked.default.bg,
2460
- borderColor: theme.components.checkbox.checked.default.border
2542
+ backgroundColor: theme.components.checkbox.primary.default.bg,
2543
+ borderColor: theme.components.checkbox.primary.default.border
2461
2544
  },
2462
2545
  boxCheckedHover: {
2463
- backgroundColor: theme.components.checkbox.checked.hover.bg,
2464
- borderColor: theme.components.checkbox.checked.hover.border
2546
+ backgroundColor: theme.components.checkbox.primary.hover.bg,
2547
+ borderColor: theme.components.checkbox.primary.hover.border
2465
2548
  },
2466
2549
  boxCheckedPressed: {
2467
- backgroundColor: theme.components.checkbox.checked.pressed.bg,
2468
- borderColor: theme.components.checkbox.checked.pressed.border
2550
+ backgroundColor: theme.components.checkbox.primary.pressed.bg,
2551
+ borderColor: theme.components.checkbox.primary.pressed.border
2469
2552
  },
2470
2553
  boxDisabled: {
2471
2554
  backgroundColor: theme.components.checkbox.disabled.bg,
@@ -2475,25 +2558,25 @@ var createStyles22 = (theme) => StyleSheet22.create({
2475
2558
  borderColor: theme.components.checkbox.error.border
2476
2559
  },
2477
2560
  checkmark: {
2478
- color: theme.components.checkbox.checked.default.fg
2561
+ color: theme.components.checkbox.primary.default.fg
2479
2562
  },
2480
2563
  checkmarkHover: {
2481
- color: theme.components.checkbox.checked.hover.fg
2564
+ color: theme.components.checkbox.primary.hover.fg
2482
2565
  },
2483
2566
  checkmarkPressed: {
2484
- color: theme.components.checkbox.checked.pressed.fg
2567
+ color: theme.components.checkbox.primary.pressed.fg
2485
2568
  },
2486
2569
  indeterminateMark: {
2487
2570
  width: "60%",
2488
2571
  height: 2,
2489
- backgroundColor: theme.components.checkbox.checked.default.fg,
2572
+ backgroundColor: theme.components.checkbox.primary.default.fg,
2490
2573
  borderRadius: theme.tokens.radius.full
2491
2574
  },
2492
2575
  indeterminateMarkHover: {
2493
- backgroundColor: theme.components.checkbox.checked.hover.fg
2576
+ backgroundColor: theme.components.checkbox.primary.hover.fg
2494
2577
  },
2495
2578
  indeterminateMarkPressed: {
2496
- backgroundColor: theme.components.checkbox.checked.pressed.fg
2579
+ backgroundColor: theme.components.checkbox.primary.pressed.fg
2497
2580
  },
2498
2581
  label: {
2499
2582
  flexShrink: 1,
@@ -2501,13 +2584,13 @@ var createStyles22 = (theme) => StyleSheet22.create({
2501
2584
  color: theme.components.checkbox.default.fg
2502
2585
  },
2503
2586
  labelChecked: {
2504
- color: theme.components.checkbox.checked.default.labelFg
2587
+ color: theme.components.checkbox.primary.default.labelFg
2505
2588
  },
2506
2589
  labelCheckedHover: {
2507
- color: theme.components.checkbox.checked.hover.labelFg
2590
+ color: theme.components.checkbox.primary.hover.labelFg
2508
2591
  },
2509
2592
  labelCheckedPressed: {
2510
- color: theme.components.checkbox.checked.pressed.labelFg
2593
+ color: theme.components.checkbox.primary.pressed.labelFg
2511
2594
  },
2512
2595
  labelSm: {
2513
2596
  fontSize: theme.tokens.typography.size.sm,
@@ -2564,12 +2647,16 @@ var Checkbox = forwardRef2(
2564
2647
  ({
2565
2648
  label,
2566
2649
  description,
2650
+ icon,
2651
+ indeterminateIcon,
2567
2652
  checked,
2568
2653
  defaultChecked = false,
2569
2654
  disabled = false,
2570
2655
  required = false,
2571
2656
  indeterminate = false,
2572
2657
  size = "md",
2658
+ color = "primary",
2659
+ labelPosition = "end",
2573
2660
  onCheckedChange,
2574
2661
  error,
2575
2662
  style,
@@ -2579,6 +2666,7 @@ var Checkbox = forwardRef2(
2579
2666
  }, ref) => {
2580
2667
  const { theme } = useTheme();
2581
2668
  const styles = useThemeStyles(createStyles22);
2669
+ const checkboxColor = theme.components.checkbox[color];
2582
2670
  const boxSizeStyle = {
2583
2671
  sm: styles.boxSm,
2584
2672
  md: styles.boxMd,
@@ -2613,7 +2701,7 @@ var Checkbox = forwardRef2(
2613
2701
  "Checkbox: an accessible label must be provided through label or accessibilityLabel."
2614
2702
  );
2615
2703
  }, [resolvedAccessibilityLabel]);
2616
- return /* @__PURE__ */ jsxs13(View19, { style: styles.container, children: [
2704
+ return /* @__PURE__ */ jsxs13(View20, { style: styles.container, children: [
2617
2705
  /* @__PURE__ */ jsx24(
2618
2706
  Pressable11,
2619
2707
  {
@@ -2630,34 +2718,44 @@ var Checkbox = forwardRef2(
2630
2718
  accessibilityLabel: resolvedAccessibilityLabel,
2631
2719
  style: [
2632
2720
  styles.wrapper,
2721
+ labelPosition === "start" && styles.wrapperLabelStart,
2633
2722
  !label && styles.iconOnly,
2634
2723
  disabled && styles.disabled,
2635
2724
  style
2636
2725
  ],
2637
2726
  children: ({ pressed }) => {
2638
2727
  const isSelected = isChecked || indeterminate;
2639
- const checkColor = disabled ? theme.components.checkbox.disabled.fg : pressed && isSelected ? theme.components.checkbox.checked.pressed.fg : theme.components.checkbox.checked.default.fg;
2728
+ const checkColor = disabled ? theme.components.checkbox.disabled.fg : pressed && isSelected ? checkboxColor.pressed.fg : checkboxColor.default.fg;
2640
2729
  return /* @__PURE__ */ jsxs13(Fragment4, { children: [
2641
2730
  /* @__PURE__ */ jsx24(
2642
- View19,
2731
+ View20,
2643
2732
  {
2644
2733
  style: [
2645
2734
  styles.box,
2646
2735
  boxSizeStyle[size],
2647
- isSelected && styles.boxChecked,
2648
- isSelected && pressed && styles.boxCheckedPressed,
2736
+ isSelected && {
2737
+ backgroundColor: checkboxColor.default.bg,
2738
+ borderColor: checkboxColor.default.border
2739
+ },
2740
+ isSelected && pressed && {
2741
+ backgroundColor: checkboxColor.pressed.bg,
2742
+ borderColor: checkboxColor.pressed.border,
2743
+ transform: [{ scale: 0.96 }]
2744
+ },
2649
2745
  hasError && styles.boxError,
2650
2746
  disabled && styles.boxDisabled
2651
2747
  ],
2652
- children: indeterminate ? /* @__PURE__ */ jsx24(
2653
- View19,
2748
+ children: indeterminate ? indeterminateIcon ?? /* @__PURE__ */ jsx24(
2749
+ View20,
2654
2750
  {
2655
2751
  style: [
2656
2752
  styles.indeterminateMark,
2657
- pressed && styles.indeterminateMarkPressed
2753
+ {
2754
+ backgroundColor: pressed ? checkboxColor.pressed.fg : checkboxColor.default.fg
2755
+ }
2658
2756
  ]
2659
2757
  }
2660
- ) : isChecked && /* @__PURE__ */ jsx24(Check, { size: iconSizeBySize[size], color: checkColor })
2758
+ ) : isChecked && (icon ?? /* @__PURE__ */ jsx24(Check, { size: iconSizeBySize[size], color: checkColor }))
2661
2759
  }
2662
2760
  ),
2663
2761
  label && /* @__PURE__ */ jsxs13(
@@ -2666,8 +2764,10 @@ var Checkbox = forwardRef2(
2666
2764
  style: [
2667
2765
  styles.label,
2668
2766
  labelSizeStyle[size],
2669
- isSelected && styles.labelChecked,
2670
- isSelected && pressed && styles.labelCheckedPressed,
2767
+ isSelected && { color: checkboxColor.default.labelFg },
2768
+ isSelected && pressed && {
2769
+ color: checkboxColor.pressed.labelFg
2770
+ },
2671
2771
  disabled && styles.labelDisabled
2672
2772
  ],
2673
2773
  children: [
@@ -2699,7 +2799,7 @@ Checkbox.displayName = "Checkbox";
2699
2799
 
2700
2800
  // src/primitives/Input/Input.tsx
2701
2801
  import { cloneElement as cloneElement6, forwardRef as forwardRef3, useState as useState6 } from "react";
2702
- import { Pressable as Pressable12, Text as Text13, TextInput, View as View20 } from "react-native";
2802
+ import { Pressable as Pressable12, Text as Text13, TextInput, View as View21 } from "react-native";
2703
2803
 
2704
2804
  // src/primitives/Input/Input.styles.ts
2705
2805
  import { StyleSheet as StyleSheet23 } from "react-native";
@@ -2958,9 +3058,9 @@ var Input = forwardRef3(
2958
3058
  required,
2959
3059
  disabled,
2960
3060
  style: containerStyle,
2961
- children: /* @__PURE__ */ jsxs14(View20, { style: styles.inputWrapper, children: [
3061
+ children: /* @__PURE__ */ jsxs14(View21, { style: styles.inputWrapper, children: [
2962
3062
  leftIcon && /* @__PURE__ */ jsx25(
2963
- View20,
3063
+ View21,
2964
3064
  {
2965
3065
  pointerEvents: "none",
2966
3066
  style: styles.leftIcon,
@@ -3028,7 +3128,7 @@ var Input = forwardRef3(
3028
3128
  )
3029
3129
  }
3030
3130
  ) : showRightIcon && rightIcon && /* @__PURE__ */ jsx25(
3031
- View20,
3131
+ View21,
3032
3132
  {
3033
3133
  pointerEvents: "none",
3034
3134
  style: styles.rightIcon,
@@ -3053,7 +3153,7 @@ import { useControllableState as useControllableState4 } from "@vellira-ui/core"
3053
3153
  import {
3054
3154
  Pressable as Pressable13,
3055
3155
  Text as Text14,
3056
- View as View21
3156
+ View as View22
3057
3157
  } from "react-native";
3058
3158
 
3059
3159
  // src/primitives/Radio/Radio.styles.ts
@@ -3242,7 +3342,7 @@ var Radio = forwardRef4(
3242
3342
  state.pressed && !resolvedDisabled && styles.pressablePressed,
3243
3343
  typeof style === "function" ? style(state) : style
3244
3344
  ];
3245
- return /* @__PURE__ */ jsxs15(View21, { style: [styles.root, containerStyle], children: [
3345
+ return /* @__PURE__ */ jsxs15(View22, { style: [styles.root, containerStyle], children: [
3246
3346
  /* @__PURE__ */ jsxs15(
3247
3347
  Pressable13,
3248
3348
  {
@@ -3260,7 +3360,7 @@ var Radio = forwardRef4(
3260
3360
  style: resolvePressableStyle,
3261
3361
  children: [
3262
3362
  /* @__PURE__ */ jsx26(
3263
- View21,
3363
+ View22,
3264
3364
  {
3265
3365
  pointerEvents: "none",
3266
3366
  style: [
@@ -3275,7 +3375,7 @@ var Radio = forwardRef4(
3275
3375
  resolvedDisabled && styles.controlDisabled
3276
3376
  ],
3277
3377
  children: resolvedChecked && /* @__PURE__ */ jsx26(
3278
- View21,
3378
+ View22,
3279
3379
  {
3280
3380
  style: [
3281
3381
  styles.indicator,
@@ -3289,7 +3389,7 @@ var Radio = forwardRef4(
3289
3389
  )
3290
3390
  }
3291
3391
  ),
3292
- (label || description) && /* @__PURE__ */ jsxs15(View21, { pointerEvents: "none", style: styles.content, children: [
3392
+ (label || description) && /* @__PURE__ */ jsxs15(View22, { pointerEvents: "none", style: styles.content, children: [
3293
3393
  label && (typeof label === "string" ? /* @__PURE__ */ jsx26(
3294
3394
  Text14,
3295
3395
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellira-ui/react-native",
3
- "version": "2.26.1",
3
+ "version": "2.28.0",
4
4
  "description": "React Native components for Vellira Design System",
5
5
  "author": "Roman Bakurov",
6
6
  "license": "MIT",