From 0eed1cfec1b0caac35930529dfd9ac92bf38606f Mon Sep 17 00:00:00 2001
From: Eric Bailey <git@esb.lol>
Date: Fri, 20 Sep 2024 11:38:51 -0500
Subject: [Neue] Buttons (#5406)

* Re-align button sizing

(cherry picked from commit bcec243bb59dfe468313d98ba61f464d9750feec)

* Use large, small, tiny

(cherry picked from commit 1dc333c2993ab7f2e0ac750c0670dcec9a7069d0)

* Tweaks
---
 src/components/Button.tsx | 148 ++++++++++++++++++++++++++++++++++------------
 1 file changed, 111 insertions(+), 37 deletions(-)

(limited to 'src/components/Button.tsx')

diff --git a/src/components/Button.tsx b/src/components/Button.tsx
index 704aa9d98..8728b88c2 100644
--- a/src/components/Button.tsx
+++ b/src/components/Button.tsx
@@ -14,7 +14,7 @@ import {
 } from 'react-native'
 import {LinearGradient} from 'expo-linear-gradient'
 
-import {android, atoms as a, flatten, select, tokens, useTheme} from '#/alf'
+import {atoms as a, flatten, select, tokens, useTheme, web} from '#/alf'
 import {Props as SVGIconProps} from '#/components/icons/common'
 import {Text} from '#/components/Typography'
 
@@ -30,7 +30,7 @@ export type ButtonColor =
   | 'gradient_sunset'
   | 'gradient_nordic'
   | 'gradient_bonfire'
-export type ButtonSize = 'tiny' | 'xsmall' | 'small' | 'medium' | 'large'
+export type ButtonSize = 'tiny' | 'small' | 'large'
 export type ButtonShape = 'round' | 'square' | 'default'
 export type VariantProps = {
   /**
@@ -343,39 +343,46 @@ export const Button = React.forwardRef<View, ButtonProps>(
 
       if (shape === 'default') {
         if (size === 'large') {
-          baseStyles.push(
-            {paddingVertical: 15},
-            a.px_2xl,
-            a.rounded_sm,
-            a.gap_md,
-          )
-        } else if (size === 'medium') {
-          baseStyles.push(
-            {paddingVertical: 12},
-            a.px_2xl,
-            a.rounded_sm,
-            a.gap_md,
-          )
+          baseStyles.push({
+            paddingVertical: 13,
+            paddingHorizontal: 20,
+            borderRadius: 8,
+            gap: 8,
+          })
         } else if (size === 'small') {
-          baseStyles.push({paddingVertical: 9}, a.px_lg, a.rounded_sm, a.gap_sm)
-        } else if (size === 'xsmall') {
-          baseStyles.push({paddingVertical: 6}, a.px_sm, a.rounded_sm, a.gap_sm)
+          baseStyles.push({
+            paddingVertical: 8,
+            paddingHorizontal: 12,
+            borderRadius: 6,
+            gap: 6,
+          })
         } else if (size === 'tiny') {
-          baseStyles.push({paddingVertical: 4}, a.px_sm, a.rounded_xs, a.gap_xs)
+          baseStyles.push({
+            paddingVertical: 4,
+            paddingHorizontal: 8,
+            borderRadius: 4,
+            gap: 4,
+          })
         }
       } else if (shape === 'round' || shape === 'square') {
         if (size === 'large') {
           if (shape === 'round') {
-            baseStyles.push({height: 54, width: 54})
+            baseStyles.push({height: 46, width: 46})
           } else {
-            baseStyles.push({height: 50, width: 50})
+            baseStyles.push({height: 44, width: 44})
           }
         } else if (size === 'small') {
-          baseStyles.push({height: 34, width: 34})
-        } else if (size === 'xsmall') {
-          baseStyles.push({height: 28, width: 28})
+          if (shape === 'round') {
+            baseStyles.push({height: 36, width: 36})
+          } else {
+            baseStyles.push({height: 34, width: 34})
+          }
         } else if (size === 'tiny') {
-          baseStyles.push({height: 20, width: 20})
+          if (shape === 'round') {
+            baseStyles.push({height: 22, width: 22})
+          } else {
+            baseStyles.push({height: 21, width: 21})
+          }
         }
 
         if (shape === 'round') {
@@ -619,11 +626,11 @@ export function useSharedButtonTextStyles() {
     }
 
     if (size === 'large') {
-      baseStyles.push(a.text_md, android({paddingBottom: 1}))
+      baseStyles.push(a.text_md, a.leading_tight, web({paddingTop: 1}))
+    } else if (size === 'small') {
+      baseStyles.push(a.text_sm, a.leading_tight, web({paddingTop: 1}))
     } else if (size === 'tiny') {
-      baseStyles.push(a.text_xs, android({paddingBottom: 1}))
-    } else {
-      baseStyles.push(a.text_sm, android({paddingBottom: 1}))
+      baseStyles.push(a.text_xs, a.leading_tight)
     }
 
     return StyleSheet.flatten(baseStyles)
@@ -643,31 +650,98 @@ export function ButtonText({children, style, ...rest}: ButtonTextProps) {
 export function ButtonIcon({
   icon: Comp,
   position,
-  size: iconSize,
+  size,
 }: {
   icon: React.ComponentType<SVGIconProps>
   position?: 'left' | 'right'
   size?: SVGIconProps['size']
 }) {
-  const {size, disabled} = useButtonContext()
+  const {size: buttonSize, disabled} = useButtonContext()
   const textStyles = useSharedButtonTextStyles()
+  const {iconSize, iconContainerSize} = React.useMemo(() => {
+    /**
+     * Pre-set icon sizes for different button sizes
+     */
+    const iconSizeShorthand =
+      size ??
+      (({
+        large: 'sm',
+        small: 'xs',
+        tiny: 'xs',
+      }[buttonSize || 'small'] || 'sm') as Exclude<
+        SVGIconProps['size'],
+        undefined
+      >)
+
+    /*
+     * Copied here from icons/common.tsx so we can tweak if we need to, but
+     * also so that we can calculate transforms.
+     */
+    const iconSize = {
+      xs: 12,
+      sm: 16,
+      md: 20,
+      lg: 24,
+      xl: 28,
+      '2xl': 32,
+    }[iconSizeShorthand]
+
+    /*
+     * Goal here is to match rendered text size so that different size icons
+     * don't increase button size
+     */
+    const iconContainerSize = {
+      large: 18,
+      small: 16,
+      tiny: 13,
+    }[buttonSize || 'small']
+
+    return {
+      iconSize,
+      iconContainerSize,
+    }
+  }, [buttonSize, size])
 
   return (
     <View
       style={[
         a.z_20,
         {
+          width: iconContainerSize,
+          height: iconContainerSize,
           opacity: disabled ? 0.7 : 1,
           marginLeft: position === 'left' ? -2 : 0,
           marginRight: position === 'right' ? -2 : 0,
         },
       ]}>
-      <Comp
-        size={
-          iconSize ?? (size === 'large' ? 'md' : size === 'tiny' ? 'xs' : 'sm')
-        }
-        style={[{color: textStyles.color, pointerEvents: 'none'}]}
-      />
+      <View
+        style={[
+          a.absolute,
+          {
+            width: iconSize,
+            height: iconSize,
+            top: '50%',
+            left: '50%',
+            transform: [
+              {
+                translateX: (iconSize / 2) * -1,
+              },
+              {
+                translateY: (iconSize / 2) * -1,
+              },
+            ],
+          },
+        ]}>
+        <Comp
+          width={iconSize}
+          style={[
+            {
+              color: textStyles.color,
+              pointerEvents: 'none',
+            },
+          ]}
+        />
+      </View>
     </View>
   )
 }
-- 
cgit 1.4.1

