Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

UI4: Card & Row Components #4593

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Jon-edge merged 2 commits into develop from jon/ui4/components
Dec 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,11 @@ exports[`FioAddressList should render with loading props 1`] = `
"cameraOverlayColor": "#000000",
"cameraOverlayOpEnd": 0.3,
"cameraOverlayOpStart": 0.7,
"cardBackgroundUi4": "rgb(22, 50, 58)",
"cardBorder": 1,
"cardBorderColor": "rgba(255, 255, 255, .1)",
"cardBorderRadius": 4,
"cardRadiusRemUi4": 1,
"clipboardPopupText": "#000000",
"confirmationSlider": "#2B333A",
"confirmationSliderArrow": "#1b2f3b",
Expand Down Expand Up @@ -449,6 +451,7 @@ exports[`FioAddressList should render with loading props 1`] = `
"titleLineDivider": "#A4C7DF",
"toggleButton": "#00f1a2",
"toggleButtonOff": "#87939E",
"touchHighlightUi4": "rgba(217, 227, 237, .75)",
"transactionListIconBackground": "#1b2f3b",
"tutorialModalUnderlay": "rgba(255, 255, 255, 0)",
"underlayColor": "#FFFFFF",
Expand Down Expand Up @@ -524,9 +527,11 @@ exports[`FioAddressList should render with loading props 1`] = `
"cameraOverlayColor": "#000000",
"cameraOverlayOpEnd": 0.3,
"cameraOverlayOpStart": 0.7,
"cardBackgroundUi4": "rgb(22, 50, 58)",
"cardBorder": 1,
"cardBorderColor": "rgba(255, 255, 255, .1)",
"cardBorderRadius": 4,
"cardRadiusRemUi4": 1,
"clipboardPopupText": "#000000",
"confirmationSlider": "#2B333A",
"confirmationSliderArrow": "#1b2f3b",
Expand Down Expand Up @@ -904,6 +909,7 @@ exports[`FioAddressList should render with loading props 1`] = `
"titleLineDivider": "#A4C7DF",
"toggleButton": "#00f1a2",
"toggleButtonOff": "#87939E",
"touchHighlightUi4": "rgba(217, 227, 237, .75)",
"transactionListIconBackground": "#1b2f3b",
"tutorialModalUnderlay": "rgba(255, 255, 255, 0)",
"underlayColor": "#FFFFFF",
Expand Down
86 changes: 86 additions & 0 deletions src/components/ui4/CardUi4.tsx
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import * as React from 'react'
import { TouchableHighlight, View } from 'react-native'

import { useHandler } from '../../hooks/useHandler'
import { triggerHaptic } from '../../util/haptic'
import { showError } from '../services/AirshipInstance'
import { cacheStyles, Theme, useTheme } from '../services/ThemeContext'
import { SectionView } from './SectionView'

export type CardType = 'default' | 'warning' | 'error'

interface Props {
children: React.ReactNode | React.ReactNode[]
icon?: React.ReactNode
onLongPress?: () => Promise<void> | void
onPress?: () => Promise<void> | void
// cardType?: CardType // TODO
}

/**
* Rounded card that automatically adds horizontal dividers between each child,
* aligned in a column layout. Adds no dividers if only one child is given.
*/
export const CardUi4 = (props: Props) => {
const { children, icon, onLongPress, onPress } = props
const theme = useTheme()
const styles = getStyles(theme)

const handlePress = useHandler(async () => {
if (onPress != null) {
triggerHaptic('impactLight')
try {
await onPress()
} catch (err) {
showError(err)
}
}
})

const handleLongPress = useHandler(async () => {
if (onLongPress != null) {
triggerHaptic('impactLight')
try {
await onLongPress()
} catch (err) {
showError(err)
}
}
})

return (
<TouchableHighlight
accessible={false}
onPress={handlePress}
onLongPress={handleLongPress}
disabled={handlePress == null && handleLongPress == null}
underlayColor={theme.touchHighlightUi4}
style={styles.cardContainer}
>
<>
{icon == null ? null : <View style={styles.iconContainer}>{icon}</View>}
<SectionView>{children}</SectionView>
</>
</TouchableHighlight>
)
}

// TODO: Adjust margin/padding so everything combines with correct layout no
// matter the combination of UI4 components.
const getStyles = cacheStyles((theme: Theme) => ({
cardContainer: {
backgroundColor: theme.cardBackgroundUi4,
borderRadius: theme.rem(theme.cardRadiusRemUi4),
margin: theme.rem(0.5),
padding: theme.rem(0.5),
flexDirection: 'row'
},
iconContainer: {
margin: theme.rem(0.25),
justifyContent: 'center',
alignContent: 'center'
},
warning: {
borderColor: theme.warningIcon
}
}))
157 changes: 157 additions & 0 deletions src/components/ui4/RowUi4.tsx
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import Clipboard from '@react-native-clipboard/clipboard'
import * as React from 'react'
import { ActivityIndicator, TouchableHighlight, View } from 'react-native'
import FontAwesomeIcon from 'react-native-vector-icons/FontAwesome'
import FontAwesome5 from 'react-native-vector-icons/FontAwesome5'
import SimpleLineIcons from 'react-native-vector-icons/SimpleLineIcons'

import { useHandler } from '../../hooks/useHandler'
import { lstrings } from '../../locales/strings'
import { triggerHaptic } from '../../util/haptic'
import { showError, showToast } from '../services/AirshipInstance'
import { cacheStyles, Theme, useTheme } from '../services/ThemeContext'
import { EdgeText } from '../themed/EdgeText'

const textHeights = {
small: 2,
medium: 3,
large: 0
}

export type RowType = 'copy' | 'editable' | 'questionable' | 'loading' | 'default' | 'touchable' | 'delete'

interface Props {
body?: string
children?: React.ReactNode
error?: boolean
onLongPress?: () => Promise<void> | void
onPress?: () => Promise<void> | void
title?: string
type?: RowType
maximumHeight?: 'small' | 'medium' | 'large'
icon?: React.ReactNode
}

export const RowUi4 = (props: Props) => {
const theme = useTheme()
const styles = getStyles(theme)

const { body, title, children, maximumHeight = 'medium', error, icon, onLongPress, onPress } = props
const { type = onLongPress == null && onPress == null ? 'default' : 'touchable' } = props

const numberOfLines = textHeights[maximumHeight]

const handlePress = useHandler(async () => {
if (type === 'copy' && body != null) {
triggerHaptic('impactLight')
Clipboard.setString(body)
showToast(lstrings.fragment_copied)
} else if (onPress != null) {
triggerHaptic('impactLight')
try {
await onPress()
} catch (err) {
showError(err)
}
}
})

const handleLongPress = useHandler(async () => {
if (onLongPress != null) {
triggerHaptic('impactLight')
try {
await onLongPress()
} catch (err) {
showError(err)
}
}
})

return type === 'loading' ? (
<View style={styles.container}>
<View style={styles.content}>
<EdgeText style={styles.textHeader}>{title}</EdgeText>
<ActivityIndicator style={styles.loader} color={theme.primaryText} size="large" />
</View>
</View>
) : (
<TouchableHighlight
accessible={false}
onPress={handlePress}
onLongPress={handleLongPress}
disabled={handlePress == null && handleLongPress == null}
underlayColor={theme.touchHighlightUi4}
>
<View style={styles.container}>
{icon == null ? null : <View style={styles.iconContainer}>{icon}</View>}
<View style={styles.content}>
{title == null ? null : (
<EdgeText disableFontScaling ellipsizeMode="tail" style={error ? styles.textHeaderError : styles.textHeader}>
{title}
</EdgeText>
)}
{children == null ? (
body == null ? null : (
<EdgeText style={styles.textBody} numberOfLines={numberOfLines} ellipsizeMode="tail">
{body}
</EdgeText>
)
) : (
children
)}
</View>
<View style={styles.rightButtonContainer}>
{type === 'touchable' && <FontAwesome5 name="chevron-right" style={styles.tappableIcon} size={theme.rem(1)} />}
{type === 'editable' && <FontAwesomeIcon name="edit" style={styles.tappableIcon} size={theme.rem(1)} />}
{type === 'copy' && <FontAwesomeIcon name="copy" style={styles.tappableIcon} size={theme.rem(1)} />}
{type === 'delete' && <FontAwesomeIcon name="times" style={styles.tappableIcon} size={theme.rem(1)} />}
{type === 'questionable' && <SimpleLineIcons name="question" style={styles.tappableIcon} size={theme.rem(1)} />}
</View>
</View>
</TouchableHighlight>
)
}

// TODO: Adjust margin/padding so everything combines with correct layout no
// matter the combination of UI4 components.
Comment on lines +115 to +116
Copy link
Contributor

@swansontec swansontec Nov 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ambitious. I assume this will happen later as more stuff gets built out?

Copy link
Collaborator Author

@Jon-edge Jon-edge Nov 24, 2023
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I tried to start doing it and then found I had to make more changes upon tackling subsequent tasks and thinking of new rules for margin handling for sub components.

Figured it would be better to just handle it at the end to make sure everything works and avoid all the rebasing, it's already almost there at this point.

swansontec reacted with thumbs up emoji
const getStyles = cacheStyles((theme: Theme) => ({
container: {
backgroundColor: theme.tileBackground,
paddingHorizontal: theme.rem(0.5),
paddingVertical: theme.rem(0.25),
flexDirection: 'row',
alignItems: 'center'
},
content: {
flex: 1
},
iconContainer: {
marginRight: theme.rem(0.5)
},
rightButtonContainer: {
justifyContent: 'center',
alignItems: 'center'
},
tappableIcon: {
color: theme.iconTappable,
marginLeft: theme.rem(0.5),
textAlign: 'center'
},
textHeader: {
color: theme.secondaryText,
fontSize: theme.rem(0.75),
paddingBottom: theme.rem(0.25),
paddingRight: theme.rem(1)
},
textHeaderError: {
color: theme.dangerText,
fontSize: theme.rem(0.75)
},
textBody: {
color: theme.primaryText,
fontSize: theme.rem(1)
},
loader: {
marginTop: theme.rem(0.25)
}
}))
59 changes: 59 additions & 0 deletions src/components/ui4/SectionView.tsx
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import * as React from 'react'
import { View } from 'react-native'

import { cacheStyles, Theme, useTheme } from '../services/ThemeContext'

interface Props {
children: React.ReactNode | React.ReactNode[]
}

/**
* View that automatically adds horizontal dividers between each child, aligned
* in a column layout. Adds no dividers if only one child is given.
*/
export const SectionView = (props: Props): JSX.Element | null => {
const { children } = props
const theme = useTheme()
const styles = getStyles(theme)

const nonNullChildren = React.Children.map(children, child => {
if (child != null) {
return child
}
})
const numChildren = React.Children.count(nonNullChildren)

if (children == null || numChildren === 0) return null

// Add a line divider between each child if there's more than one:
return (
<View style={styles.container}>
{numChildren === 1
? nonNullChildren
: React.Children.map(nonNullChildren, (child, index) => {
if (index < numChildren - 1) {
return (
<>
{child}
<View style={styles.divider} />
</>
)
}
return child
})}
</View>
)
}

const getStyles = cacheStyles((theme: Theme) => ({
container: {
flexDirection: 'column',
flex: 1
},
divider: {
height: theme.thinLineWidth,
marginHorizontal: theme.rem(0.5),
borderBottomWidth: theme.thinLineWidth,
borderBottomColor: theme.lineDivider
}
}))
13 changes: 11 additions & 2 deletions src/theme/variables/edgeDark.ts
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,10 @@ const palette = {
QuicksandRegular: 'Quicksand-Regular',
QuicksandMedium: 'Quicksand-Medium',
QuicksandSemiBold: 'Quicksand-SemiBold',
QuicksandBold: 'Quicksand-Bold'
QuicksandBold: 'Quicksand-Bold',

// UI4 palette
teal: 'rgb(22, 50, 58)'
}

const deviceWidth = Dimensions.get('window').width
Expand Down Expand Up @@ -391,5 +394,11 @@ export const edgeDark: Theme = {
fioAddressLogo: fioAddressLogo,
walletListSlideTutorialImage: walletListSlidingTutorial,

guiPluginLogoMoonpay: guiPluginLogoMoonpay
guiPluginLogoMoonpay: guiPluginLogoMoonpay,

// UI 4.0:
cardBackgroundUi4: palette.teal,
cardRadiusRemUi4: 1,

touchHighlightUi4: palette.lightGrayOp75
}
8 changes: 7 additions & 1 deletion src/theme/variables/edgeLight.ts
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -391,5 +391,11 @@ export const edgeLight: Theme = {
fioAddressLogo: fioAddressLogo,
walletListSlideTutorialImage: walletListSlidingTutorial,

guiPluginLogoMoonpay: guiPluginLogoMoonpay
guiPluginLogoMoonpay: guiPluginLogoMoonpay,

// UI 4.0:
cardBackgroundUi4: palette.mutedGray,
cardRadiusRemUi4: 1,

touchHighlightUi4: palette.lightGrayOp75
}
Loading

AltStyle によって変換されたページ (->オリジナル) /