Add some form elements

This commit is contained in:
Eric Bailey
2024-01-09 17:37:43 -06:00
parent b5474eb61e
commit 83876b62e1
6 changed files with 189 additions and 11 deletions
+5 -7
View File
@@ -4,14 +4,10 @@ import type {Mutable} from '#/alf/types'
export type ThemeName = 'light' | 'dark'
export type ReadonlyTheme = typeof light
export type Theme = Mutable<ReadonlyTheme>
export type ReadonlyPalette = typeof lightPalette
export type Palette = Mutable<ReadonlyPalette>
export type Palette = {
primary: string
positive: string
negative: string
}
export const lightPalette: Palette = {
export const lightPalette = {
primary: tokens.color.blue_500,
positive: tokens.color.green_500,
negative: tokens.color.red_500,
@@ -24,6 +20,7 @@ export const darkPalette: Palette = {
} as const
export const light = {
name: 'light',
palette: lightPalette,
atoms: {
text: {
@@ -66,6 +63,7 @@ export const light = {
}
export const dark: Theme = {
name: 'dark',
palette: darkPalette,
atoms: {
text: {
+5 -2
View File
@@ -7,7 +7,7 @@ import {
ViewStyle,
} from 'react-native'
import {atoms, tokens, web, native} from '#/alf'
import {useTheme, atoms, tokens, web, native} from '#/alf'
export type ButtonType = 'primary' | 'secondary' | 'negative'
export type ButtonSize = 'small' | 'large'
@@ -217,6 +217,8 @@ export function ButtonText({
disabled,
...rest
}: ButtonTextProps) {
const t = useTheme()
const textStyles = React.useMemo(() => {
const baseStyles = []
@@ -244,6 +246,7 @@ export function ButtonText({
break
}
default:
baseStyles.push(t.atoms.text)
}
switch (size) {
@@ -267,7 +270,7 @@ export function ButtonText({
}
return baseStyles
}, [type, size, disabled])
}, [t, type, size, disabled])
return (
<Text
+37
View File
@@ -0,0 +1,37 @@
import React from 'react'
import {View} from 'react-native'
import {atoms} from '#/alf'
/**
* NOT FINISHED, just here as a reference
*/
export function InputGroup(props: React.PropsWithChildren<{}>) {
const children = React.Children.toArray(props.children)
const total = children.length
return (
<View style={[atoms.w_full]}>
{children.map((child, i) => {
return React.isValidElement(child) ? (
<React.Fragment key={i}>
{React.cloneElement(child, {
// @ts-ignore
style: [
...(Array.isArray(child.props?.style)
? child.props.style
: [child.props.style || {}]),
{
borderTopLeftRadius: i > 0 ? 0 : undefined,
borderTopRightRadius: i > 0 ? 0 : undefined,
borderBottomLeftRadius: i < total - 1 ? 0 : undefined,
borderBottomRightRadius: i < total - 1 ? 0 : undefined,
borderBottomWidth: i < total - 1 ? 0 : undefined,
},
],
})}
</React.Fragment>
) : null
})}
</View>
)
}
+125
View File
@@ -0,0 +1,125 @@
import React from 'react'
import {View, TextInput, TextInputProps, TextStyle} from 'react-native'
import {useTheme, atoms, web, tokens} from '#/alf'
type Props = Omit<TextInputProps, 'placeholder'> & {
placeholder: string
hasError?: boolean
icon?: React.FunctionComponent<any>
}
export function InputText({hasError, icon: Icon, ...props}: Props) {
const t = useTheme()
const [state, setState] = React.useState({
hovered: false,
focused: false,
})
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])
const {inputStyles, iconStyles} = React.useMemo(() => {
const input: TextStyle[] = []
const icon: TextStyle[] = []
if (Icon) {
input.push({
paddingLeft: 40,
})
}
if (hasError) {
input.push({
borderColor: tokens.color.red_200,
})
icon.push({
color: tokens.color.red_400,
})
}
if (state.hovered || state.focused) {
input.push({
borderColor: t.atoms.border_contrast_500.borderColor,
})
if (hasError) {
input.push({
borderColor: tokens.color.red_500,
})
}
}
return {inputStyles: input, iconStyles: icon}
}, [t, state, hasError, Icon])
return (
<View style={[atoms.relative, atoms.w_full]}>
<TextInput
{...props}
onFocus={onFocus}
onBlur={onBlur}
{...web({
onMouseEnter: onHoverIn,
onMouseLeave: onHoverOut,
})}
placeholderTextColor={t.atoms.text_contrast_500.color}
style={[
t.name === 'dark' ? t.atoms.bg_contrast_100 : t.atoms.bg,
atoms.w_full,
atoms.px_lg,
atoms.py_md,
atoms.rounded_sm,
t.atoms.border,
t.atoms.text,
{borderWidth: 2},
web({
paddingTop: atoms.pt_md.paddingTop - 1,
}),
...inputStyles,
...(Array.isArray(props.style) ? props.style : [props.style]),
]}
/>
{Icon && (
<Icon
style={[
atoms.absolute,
atoms.inset_0,
{color: t.atoms.border_contrast_500.borderColor},
{
left: atoms.px_md.paddingLeft,
right: 'auto',
width: 20,
marginVertical: 'auto',
pointerEvents: 'none',
},
...iconStyles,
]}
/>
)}
</View>
)
}
+4 -2
View File
@@ -1,4 +1,5 @@
import React from 'react'
import {StyleSheet} from 'react-native'
import Svg, {
Path,
Defs,
@@ -19,7 +20,8 @@ type Props = {
export const Logo = React.forwardRef(function LogoImpl(props: Props, ref) {
const {fill, ...rest} = props
const gradient = fill === 'sky'
const _fill = gradient ? 'url(#sky)' : fill || colors.blue3
const styles = StyleSheet.flatten(props.style)
const _fill = gradient ? 'url(#sky)' : fill || styles?.color || colors.blue3
// @ts-ignore it's fiiiiine
const size = parseInt(rest.width || 32)
return (
@@ -29,7 +31,7 @@ export const Logo = React.forwardRef(function LogoImpl(props: Props, ref) {
ref={ref}
viewBox="0 0 64 57"
{...rest}
style={{width: size, height: size * ratio}}>
style={[{width: size, height: size * ratio}, styles]}>
{gradient && (
<Defs>
<LinearGradient id="sky" x1="0" y1="0" x2="0" y2="1">
+13
View File
@@ -9,6 +9,8 @@ import {atoms as a, useTheme, useBreakpoints, ThemeProvider as Alf} from '#/alf'
import {Button, ButtonText} from '#/view/com/Button'
import {Link} from '#/view/com/Link'
import {Text, H1, H2, H3, H4, H5, H6} from '#/view/com/Typography'
import {InputText} from '#/view/com/forms/InputText'
import {Logo} from '#/view/icons/Logo'
function ThemeSelector() {
const setColorMode = useSetColorMode()
@@ -212,6 +214,16 @@ export function Buttons() {
)
}
function Forms() {
return (
<View style={[a.gap_md, a.align_start]}>
<InputText placeholder="Type here" />
<InputText placeholder="Type here" icon={Logo} />
<InputText hasError placeholder="Type here" icon={Logo} />
</View>
)
}
export function DebugScreen() {
const t = useTheme()
@@ -221,6 +233,7 @@ export function DebugScreen() {
<View style={[a.p_xl, a.gap_xxl, {paddingBottom: 200}]}>
<ThemeSelector />
<Forms />
<Buttons />
<Alf theme="light">