Usability improvements
This commit is contained in:
@@ -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",
|
||||||
|
|||||||
@@ -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}>
|
||||||
|
|||||||
@@ -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,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user