From a5b474895a27bb36381cca6a580dc19e4c4b10c2 Mon Sep 17 00:00:00 2001
From: Eric Bailey <git@esb.lol>
Date: Mon, 8 Jan 2024 19:43:56 -0600
Subject: Application Layout Framework (#1732)

* Initial library setup

* Add docblocks

* Some cleanup

* New storybook

* Playing around

* Remove silly test, use for...in

* Memo

* Memo

* Add hooks example

* Tweak colors, bit of cleanup

* Improve macro handling

* Add some more examples

* Rename for better diff

* Cleanup

* Add nested context example

* Add todo

* Less break more perf

* Buttons, you get the idea

* Fix test

* Remove temp colors

* Add a few more common macros

* Docs

* Perf improvements

* Alf go brrrr

* Update breakpoint handling

* I think it'll work

* Better naming, better code

* Fix typo

* Some renaming

* More complete pass at Tailwind naming

* Build out storybook

* Playing around with curves

* Revert "Playing around with curves"

This reverts commit 6b0e0e5c9d842a2d9af31b53affe2f6291c3fa0d.

* Smooth brain

* Remove outdated docs

* Some docs, fix line-height values, export tokens
---
 src/view/com/Button.tsx | 204 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 204 insertions(+)
 create mode 100644 src/view/com/Button.tsx

(limited to 'src/view/com/Button.tsx')

diff --git a/src/view/com/Button.tsx b/src/view/com/Button.tsx
new file mode 100644
index 000000000..d1f70d4ae
--- /dev/null
+++ b/src/view/com/Button.tsx
@@ -0,0 +1,204 @@
+import React from 'react'
+import {Pressable, Text, PressableProps, TextProps} from 'react-native'
+import * as tokens from '#/alf/tokens'
+import {atoms} from '#/alf'
+
+export type ButtonType =
+  | 'primary'
+  | 'secondary'
+  | 'tertiary'
+  | 'positive'
+  | 'negative'
+export type ButtonSize = 'small' | 'large'
+
+export type VariantProps = {
+  type?: ButtonType
+  size?: ButtonSize
+}
+type ButtonState = {
+  pressed: boolean
+  hovered: boolean
+  focused: boolean
+}
+export type ButtonProps = Omit<PressableProps, 'children'> &
+  VariantProps & {
+    children:
+      | ((props: {
+          state: ButtonState
+          type?: ButtonType
+          size?: ButtonSize
+        }) => React.ReactNode)
+      | React.ReactNode
+      | string
+  }
+export type ButtonTextProps = TextProps & VariantProps
+
+export function Button({children, style, type, size, ...rest}: ButtonProps) {
+  const {baseStyles, hoverStyles} = React.useMemo(() => {
+    const baseStyles = []
+    const hoverStyles = []
+
+    switch (type) {
+      case 'primary':
+        baseStyles.push({
+          backgroundColor: tokens.color.blue_500,
+        })
+        break
+      case 'secondary':
+        baseStyles.push({
+          backgroundColor: tokens.color.gray_200,
+        })
+        hoverStyles.push({
+          backgroundColor: tokens.color.gray_100,
+        })
+        break
+      default:
+    }
+
+    switch (size) {
+      case 'large':
+        baseStyles.push(
+          atoms.py_md,
+          atoms.px_xl,
+          atoms.rounded_md,
+          atoms.gap_sm,
+        )
+        break
+      case 'small':
+        baseStyles.push(
+          atoms.py_sm,
+          atoms.px_md,
+          atoms.rounded_sm,
+          atoms.gap_xs,
+        )
+        break
+      default:
+    }
+
+    return {
+      baseStyles,
+      hoverStyles,
+    }
+  }, [type, size])
+
+  const [state, setState] = React.useState({
+    pressed: false,
+    hovered: false,
+    focused: false,
+  })
+
+  const onPressIn = React.useCallback(() => {
+    setState(s => ({
+      ...s,
+      pressed: true,
+    }))
+  }, [setState])
+  const onPressOut = React.useCallback(() => {
+    setState(s => ({
+      ...s,
+      pressed: false,
+    }))
+  }, [setState])
+  const onHoverIn = React.useCallback(() => {
+    setState(s => ({
+      ...s,
+      hovered: true,
+    }))
+  }, [setState])
+  const onHoverOut = React.useCallback(() => {
+    setState(s => ({
+      ...s,
+      hovered: false,
+    }))
+  }, [setState])
+  const onFocus = React.useCallback(() => {
+    setState(s => ({
+      ...s,
+      focused: true,
+    }))
+  }, [setState])
+  const onBlur = React.useCallback(() => {
+    setState(s => ({
+      ...s,
+      focused: false,
+    }))
+  }, [setState])
+
+  return (
+    <Pressable
+      {...rest}
+      style={state => [
+        atoms.flex_row,
+        atoms.align_center,
+        ...baseStyles,
+        ...(state.hovered ? hoverStyles : []),
+        typeof style === 'function' ? style(state) : style,
+      ]}
+      onPressIn={onPressIn}
+      onPressOut={onPressOut}
+      onHoverIn={onHoverIn}
+      onHoverOut={onHoverOut}
+      onFocus={onFocus}
+      onBlur={onBlur}>
+      {typeof children === 'string' ? (
+        <ButtonText type={type} size={size}>
+          {children}
+        </ButtonText>
+      ) : typeof children === 'function' ? (
+        children({state, type, size})
+      ) : (
+        children
+      )}
+    </Pressable>
+  )
+}
+
+export function ButtonText({
+  children,
+  style,
+  type,
+  size,
+  ...rest
+}: ButtonTextProps) {
+  const textStyles = React.useMemo(() => {
+    const base = []
+
+    switch (type) {
+      case 'primary':
+        base.push({color: tokens.color.white})
+        break
+      case 'secondary':
+        base.push({
+          color: tokens.color.gray_700,
+        })
+        break
+      default:
+    }
+
+    switch (size) {
+      case 'small':
+        base.push(atoms.text_sm, {paddingBottom: 1})
+        break
+      case 'large':
+        base.push(atoms.text_md, {paddingBottom: 1})
+        break
+      default:
+    }
+
+    return base
+  }, [type, size])
+
+  return (
+    <Text
+      {...rest}
+      style={[
+        atoms.flex_1,
+        atoms.font_semibold,
+        atoms.text_center,
+        ...textStyles,
+        style,
+      ]}>
+      {children}
+    </Text>
+  )
+}
-- 
cgit 1.4.1

