Usability improvements

This commit is contained in:
Eric Bailey
2024-03-05 14:09:48 -06:00
parent f1f8b2dd65
commit 250258da9b
5 changed files with 66 additions and 25 deletions
+1
View File
@@ -149,6 +149,7 @@
"react-avatar-editor": "^13.0.0", "react-avatar-editor": "^13.0.0",
"react-circular-progressbar": "^2.1.0", "react-circular-progressbar": "^2.1.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-keyed-flatten-children": "^3.0.0",
"react-native": "0.73.2", "react-native": "0.73.2",
"react-native-appstate-hook": "^1.0.6", "react-native-appstate-hook": "^1.0.6",
"react-native-drawer-layout": "^4.0.0-alpha.3", "react-native-drawer-layout": "^4.0.0-alpha.3",
+23 -7
View File
@@ -1,5 +1,6 @@
import React from 'react' import React from 'react'
import {View, Pressable} from 'react-native' import {View, Pressable} from 'react-native'
import flattenReactChildren from 'react-keyed-flatten-children'
import {logger} from '#/logger' import {logger} from '#/logger'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
@@ -80,21 +81,26 @@ export function Trigger({children, label}: TriggerProps) {
} }
export function Outer({children}: React.PropsWithChildren<{}>) { export function Outer({children}: React.PropsWithChildren<{}>) {
const {control} = React.useContext(Context) const context = React.useContext(Context)
return ( return (
<Dialog.Outer control={control}> <Dialog.Outer control={context.control}>
<Dialog.Handle /> <Dialog.Handle />
{/* Re-wrap with context since Dialogs are portal-ed to root */}
<Context.Provider value={context}>
<Dialog.ScrollableInner label="Menu TODO"> <Dialog.ScrollableInner label="Menu TODO">
<View style={[a.gap_lg]}>{children}</View> <View style={[a.gap_lg]}>{children}</View>
<View style={{height: a.gap_lg.gap}} /> <View style={{height: a.gap_lg.gap}} />
</Dialog.ScrollableInner> </Dialog.ScrollableInner>
</Context.Provider>
</Dialog.Outer> </Dialog.Outer>
) )
} }
export function Item({children, label, style, onPress}: ItemProps) { export function Item({children, label, style, onPress, ...rest}: ItemProps) {
const t = useTheme() const t = useTheme()
const {control} = React.useContext(Context)
const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState() const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState()
const { const {
state: pressed, state: pressed,
@@ -104,9 +110,16 @@ export function Item({children, label, style, onPress}: ItemProps) {
return ( return (
<Pressable <Pressable
{...rest}
accessibilityHint="" accessibilityHint=""
accessibilityLabel={label} accessibilityLabel={label}
onPress={onPress} onPress={e => {
onPress(e)
if (!e.defaultPrevented) {
control?.close()
}
}}
onFocus={onFocus} onFocus={onFocus}
onBlur={onBlur} onBlur={onBlur}
onPressIn={onPressIn} onPressIn={onPressIn}
@@ -115,12 +128,12 @@ export function Item({children, label, style, onPress}: ItemProps) {
a.flex_row, a.flex_row,
a.align_center, a.align_center,
a.gap_sm, a.gap_sm,
a.p_md, a.px_md,
a.rounded_md, a.rounded_md,
a.border, a.border,
t.atoms.bg_contrast_25, t.atoms.bg_contrast_25,
t.atoms.border_contrast_low, t.atoms.border_contrast_low,
{minHeight: 48}, {minHeight: 44, paddingVertical: 10},
style, style,
(focused || pressed) && [t.atoms.bg_contrast_50], (focused || pressed) && [t.atoms.bg_contrast_50],
]}> ]}>
@@ -133,7 +146,10 @@ export function ItemText({children, style}: ItemTextProps) {
const t = useTheme() const t = useTheme()
return ( return (
<Text <Text
numberOfLines={1}
ellipsizeMode="middle"
style={[ style={[
a.flex_1,
a.text_md, a.text_md,
a.font_bold, a.font_bold,
t.atoms.text_contrast_medium, t.atoms.text_contrast_medium,
@@ -161,7 +177,7 @@ export function Group({children, style}: GroupProps) {
t.atoms.border_contrast_low, t.atoms.border_contrast_low,
style, style,
]}> ]}>
{React.Children.toArray(children).map((child, i) => { {flattenReactChildren(children).map((child, i) => {
// ignore null children, like Dividers // ignore null children, like Dividers
return React.isValidElement(child) && child.props.children ? ( return React.isValidElement(child) && child.props.children ? (
<React.Fragment key={i}> <React.Fragment key={i}>
+23 -9
View File
@@ -140,8 +140,9 @@ export function Outer({children}: React.PropsWithChildren<{}>) {
) )
} }
export function Item({children, label, onPress}: ItemProps) { export function Item({children, label, onPress, ...rest}: ItemProps) {
const t = useTheme() const t = useTheme()
const {control} = React.useContext(Context)
const { const {
state: hovered, state: hovered,
onIn: onMouseEnter, onIn: onMouseEnter,
@@ -150,12 +151,23 @@ export function Item({children, label, onPress}: ItemProps) {
const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState() const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState()
return ( return (
<DropdownMenu.Item asChild onSelect={onPress}> <DropdownMenu.Item asChild>
<Pressable <Pressable
{...rest}
className="radix-dropdown-item" className="radix-dropdown-item"
accessibilityHint="" accessibilityHint=""
accessibilityLabel={label} accessibilityLabel={label}
onPress={onPress} onPress={e => {
onPress(e)
/**
* Ported forward from Radix
* @see https://www.radix-ui.com/primitives/docs/components/dropdown-menu#item
*/
if (!e.defaultPrevented) {
control.close()
}
}}
onFocus={onFocus} onFocus={onFocus}
onBlur={onBlur} onBlur={onBlur}
style={flatten([ style={flatten([
@@ -163,9 +175,8 @@ export function Item({children, label, onPress}: ItemProps) {
a.align_center, a.align_center,
a.gap_sm, a.gap_sm,
a.py_sm, a.py_sm,
a.px_sm,
a.rounded_xs, a.rounded_xs,
{minHeight: 36}, {minHeight: 32, paddingHorizontal: 10},
web({outline: 0}), web({outline: 0}),
(hovered || focused) && [ (hovered || focused) && [
web({outline: '0 !important'}), web({outline: '0 !important'}),
@@ -185,7 +196,7 @@ export function Item({children, label, onPress}: ItemProps) {
export function ItemText({children, style}: ItemTextProps) { export function ItemText({children, style}: ItemTextProps) {
const t = useTheme() const t = useTheme()
return ( return (
<Text style={[a.font_bold, t.atoms.text_contrast_high, style]}> <Text style={[a.flex_1, a.font_bold, t.atoms.text_contrast_high, style]}>
{children} {children}
</Text> </Text>
) )
@@ -198,9 +209,12 @@ export function ItemIcon({icon: Comp, position = 'left'}: ItemIconProps) {
size="md" size="md"
fill={t.atoms.text_contrast_medium.color} fill={t.atoms.text_contrast_medium.color}
style={[ style={[
{ position === 'left' && {
marginLeft: position === 'left' ? -2 : 0, marginLeft: -2,
marginRight: position === 'right' ? -2 : 0, },
position === 'right' && {
marginRight: -2,
marginLeft: 12,
}, },
]} ]}
/> />
+5 -2
View File
@@ -1,6 +1,7 @@
import React from 'react' import React from 'react'
import {Props as SVGIconProps} from '#/components/icons/common' import {GestureResponderEvent, PressableProps} from 'react-native'
import {Props as SVGIconProps} from '#/components/icons/common'
import * as Dialog from '#/components/Dialog' import * as Dialog from '#/components/Dialog'
import {TextStyleProp, ViewStyleProp} from '#/alf' import {TextStyleProp, ViewStyleProp} from '#/alf'
@@ -46,10 +47,12 @@ export type TriggerChildProps =
handlers: {} handlers: {}
} }
// TODO test id
export type ItemProps = React.PropsWithChildren< export type ItemProps = React.PropsWithChildren<
Omit<PressableProps, 'style'> &
ViewStyleProp & { ViewStyleProp & {
label: string label: string
onPress: () => void onPress: (e: GestureResponderEvent) => void
} }
> >
+7
View File
@@ -18458,6 +18458,13 @@ react-is@^17.0.1:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
react-keyed-flatten-children@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/react-keyed-flatten-children/-/react-keyed-flatten-children-3.0.0.tgz#b6ad0bde437d3ab86c8af3a1902d164be2a29d67"
integrity sha512-tSH6gvOyQjt3qtjG+kU9sTypclL1672yjpVufcE3aHNM0FhvjBUQZqsb/awIux4zEuVC3k/DP4p0GdTT/QUt/Q==
dependencies:
react-is "^18.2.0"
react-native-appstate-hook@^1.0.6: react-native-appstate-hook@^1.0.6:
version "1.0.6" version "1.0.6"
resolved "https://registry.yarnpkg.com/react-native-appstate-hook/-/react-native-appstate-hook-1.0.6.tgz#cbc16e7b89cfaea034cabd999f00e99053cabd06" resolved "https://registry.yarnpkg.com/react-native-appstate-hook/-/react-native-appstate-hook-1.0.6.tgz#cbc16e7b89cfaea034cabd999f00e99053cabd06"