Add some menu items

This commit is contained in:
Eric Bailey
2024-03-04 19:04:36 -06:00
parent aaae7c0717
commit 0f3bf15729
6 changed files with 257 additions and 26 deletions
+1
View File
@@ -213,6 +213,7 @@
}
/* NativeDropdown component */
.radix-dropdown-item:focus,
.nativeDropdown-item:focus {
outline: none;
}
+103 -4
View File
@@ -1,10 +1,20 @@
import React from 'react'
import {View, Pressable} from 'react-native'
import {atoms as a, useTheme, ViewStyleProp} from '#/alf'
import * as Dialog from '#/components/Dialog'
import {useInteractionState} from '#/components/hooks/useInteractionState'
import {Text} from '#/components/Typography'
import {ContextType, TriggerChildProps} from '#/components/Menu/types'
import {Context} from '#/components/Menu/context'
import {
ContextType,
TriggerChildProps,
ItemProps,
GroupProps,
ItemTextProps,
ItemIconProps,
} from '#/components/Menu/types'
export {useDialogControl as useMenuControl} from '#/components/Dialog'
@@ -27,7 +37,7 @@ export function Root({
export function Trigger({
children,
}: {
}: ViewStyleProp & {
children(props: TriggerChildProps): React.ReactNode
}) {
const {control} = React.useContext(Context)
@@ -71,10 +81,99 @@ export function Outer({children}: React.PropsWithChildren<{}>) {
<Dialog.Outer control={control}>
<Dialog.Handle />
<Dialog.ScrollableInner label="Menu TODO">
{children}
<View style={[a.gap_lg]}>{children}</View>
<View style={{height: a.gap_lg.gap}} />
</Dialog.ScrollableInner>
</Dialog.Outer>
)
}
export function Button({}: {}) {}
export function Item({children, label, style, onPress}: ItemProps) {
const t = useTheme()
const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState()
const {
state: pressed,
onIn: onPressIn,
onOut: onPressOut,
} = useInteractionState()
return (
<Pressable
accessibilityHint=""
accessibilityLabel={label}
onPress={onPress}
onFocus={onFocus}
onBlur={onBlur}
onPressIn={onPressIn}
onPressOut={onPressOut}
style={[
a.flex_row,
a.align_center,
a.gap_sm,
a.p_md,
a.rounded_md,
t.atoms.bg_contrast_25,
{minHeight: 48},
style,
(focused || pressed) && [t.atoms.bg_contrast_50],
]}>
{children}
</Pressable>
)
}
export function ItemText({children, style}: ItemTextProps) {
const t = useTheme()
return (
<Text
style={[
a.text_md,
a.font_bold,
t.atoms.text_contrast_medium,
{paddingTop: 2},
style,
]}>
{children}
</Text>
)
}
export function ItemIcon({icon: Comp}: ItemIconProps) {
const t = useTheme()
return <Comp size="lg" fill={t.atoms.text_contrast_medium.color} />
}
export function Group({children, style}: GroupProps) {
const t = useTheme()
return (
<View
style={[
a.rounded_md,
a.overflow_hidden,
a.border,
t.atoms.border_contrast_low,
style,
]}>
{React.Children.toArray(children).map((child, i) => {
// ignore null children, like Dividers
return React.isValidElement(child) && child.props.children ? (
<React.Fragment>
{i > 0 ? (
<View style={[a.border_b, t.atoms.border_contrast_low]} />
) : null}
{React.cloneElement(child, {
// @ts-ignore
style: {
borderRadius: 0,
},
})}
</React.Fragment>
) : null
})}
</View>
)
}
export function Divider() {
return null
}
+109 -11
View File
@@ -1,12 +1,20 @@
import React from 'react'
import {Pressable} from 'react-native'
import {View, Pressable} from 'react-native'
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
import * as Dialog from '#/components/Dialog'
import {useInteractionState} from '#/components/hooks/useInteractionState'
import {flatten, web} from '#/alf'
import {atoms as a, useTheme, flatten, web, ViewStyleProp} from '#/alf'
import {Text} from '#/components/Typography'
import {ContextType, TriggerChildProps} from '#/components/Menu/types'
import {
ContextType,
TriggerChildProps,
ItemProps,
GroupProps,
ItemTextProps,
ItemIconProps,
} from '#/components/Menu/types'
import {Context} from '#/components/Menu/context'
export function useMenuControl(): Dialog.DialogControlProps {
@@ -44,7 +52,8 @@ export function Root({
export function Trigger({
children,
}: {
style,
}: ViewStyleProp & {
children(props: TriggerChildProps): React.ReactNode
}) {
const {
@@ -59,7 +68,7 @@ export function Trigger({
<Pressable
onFocus={onFocus}
onBlur={onBlur}
style={flatten([web({outline: 0})])}
style={flatten([style, web({outline: 0})])}
{...web({
onMouseEnter,
onMouseLeave,
@@ -79,16 +88,105 @@ export function Trigger({
)
}
export function Outer(_props: React.PropsWithChildren<{}>) {
export function Outer({children}: React.PropsWithChildren<{}>) {
const t = useTheme()
return (
<DropdownMenu.Portal>
<DropdownMenu.Content sideOffset={5}>
<DropdownMenu.Item>
New Tab <div className="RightSlot">+T</div>
</DropdownMenu.Item>
<DropdownMenu.Content sideOffset={5} loop>
<View style={[a.rounded_sm, t.atoms.bg_contrast_50, {padding: 6}]}>
{children}
</View>
<DropdownMenu.Arrow
className="DropdownMenuArrow"
fill={t.atoms.bg_contrast_50.backgroundColor}
/>
</DropdownMenu.Content>
</DropdownMenu.Portal>
)
}
export function Button({}: {}) {}
export function Item({children, label, onPress}: ItemProps) {
const t = useTheme()
const {
state: hovered,
onIn: onMouseEnter,
onOut: onMouseLeave,
} = useInteractionState()
const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState()
return (
<DropdownMenu.Item asChild onSelect={onPress}>
<Pressable
className="radix-dropdown-item"
accessibilityHint=""
accessibilityLabel={label}
onFocus={onFocus}
onBlur={onBlur}
style={flatten([
a.flex_row,
a.align_center,
a.gap_sm,
a.py_sm,
a.px_md,
a.rounded_xs,
{minHeight: 36},
web({outline: 0}),
(hovered || focused) && [
web({outline: '0 !important'}),
t.atoms.bg_contrast_25,
],
])}
{...web({
onMouseEnter,
onMouseLeave,
})}>
{children}
</Pressable>
</DropdownMenu.Item>
)
}
export function ItemText({children, style}: ItemTextProps) {
const t = useTheme()
return (
<Text style={[a.font_bold, t.atoms.text_contrast_high, style]}>
{children}
</Text>
)
}
export function ItemIcon({icon: Comp, position = 'left'}: ItemIconProps) {
const t = useTheme()
return (
<Comp
size="md"
fill={t.atoms.text_contrast_medium.color}
style={[
{
marginLeft: position === 'left' ? -2 : 0,
marginRight: position === 'right' ? -2 : 0,
},
]}
/>
)
}
export function Group({children}: GroupProps) {
return children
}
export function Divider() {
const t = useTheme()
return (
<DropdownMenu.Separator
style={{
height: 1,
backgroundColor: t.atoms.bg_contrast_100.backgroundColor,
marginTop: 6,
marginBottom: 6,
}}
/>
)
}
+19
View File
@@ -1,4 +1,8 @@
import React from 'react'
import {Props as SVGIconProps} from '#/components/icons/common'
import * as Dialog from '#/components/Dialog'
import {TextStyleProp, ViewStyleProp} from '#/alf'
export type ContextType = {
control: Dialog.DialogOuterProps['control'] | null
@@ -37,3 +41,18 @@ export type TriggerChildProps =
}
handlers: {}
}
export type ItemProps = React.PropsWithChildren<
ViewStyleProp & {
label: string
onPress: () => void
}
>
export type ItemTextProps = React.PropsWithChildren<TextStyleProp & {}>
export type ItemIconProps = React.PropsWithChildren<{
icon: React.ComponentType<SVGIconProps>
position?: 'left' | 'right'
}>
export type GroupProps = React.PropsWithChildren<ViewStyleProp & {}>
+24 -11
View File
@@ -4,6 +4,7 @@ import {View} from 'react-native'
import {atoms as a} from '#/alf'
import {Text} from '#/components/Typography'
import * as Menu from '#/components/Menu'
import {MagnifyingGlass2_Stroke2_Corner0_Rounded as Search} from '#/components/icons/MagnifyingGlass2'
// import {useDialogStateControlContext} from '#/state/dialogs'
export function Menus() {
@@ -11,18 +12,30 @@ export function Menus() {
return (
<View style={[a.gap_md]}>
<Menu.Root>
<Menu.Trigger>
{({state, handlers}) => {
console.log(state.hovered, state.focused, state.pressed)
return <Text {...handlers}>Open</Text>
}}
</Menu.Trigger>
<View style={[a.flex_row, a.align_start]}>
<Menu.Root>
<Menu.Trigger style={[a.flex_1]}>
{({handlers}) => {
return <Text {...handlers}>Open</Text>
}}
</Menu.Trigger>
<Menu.Outer>
<Text>Open</Text>
</Menu.Outer>
</Menu.Root>
<Menu.Outer>
<Menu.Group>
<Menu.Item label="Click me" onPress={() => {}}>
<Menu.ItemIcon icon={Search} />
<Menu.ItemText>Click me</Menu.ItemText>
</Menu.Item>
<Menu.Divider />
<Menu.Item label="Another item" onPress={() => {}}>
<Menu.ItemText>Another item</Menu.ItemText>
</Menu.Item>
</Menu.Group>
</Menu.Outer>
</Menu.Root>
</View>
</View>
)
}
+1
View File
@@ -217,6 +217,7 @@
}
/* NativeDropdown component */
.radix-dropdown-item:focus,
.nativeDropdown-item:focus {
outline: none;
}