diff --git a/packages/k2-alpine/src/assets/icons/arrow_down.svg b/packages/k2-alpine/src/assets/icons/arrow_down.svg
new file mode 100644
index 0000000000..00baead390
--- /dev/null
+++ b/packages/k2-alpine/src/assets/icons/arrow_down.svg
@@ -0,0 +1,3 @@
+
diff --git a/packages/k2-alpine/src/components/Button/Button.stories.tsx b/packages/k2-alpine/src/components/Button/Button.stories.tsx
index b7f11f485d..96e5aaf6dd 100644
--- a/packages/k2-alpine/src/components/Button/Button.stories.tsx
+++ b/packages/k2-alpine/src/components/Button/Button.stories.tsx
@@ -5,6 +5,7 @@ import { ScrollView, Text, View } from '../Primitives'
import Link from '../../utils/Link'
import { useTheme } from '../..'
import { Button, ButtonSize, ButtonType } from './Button'
+import { FilterButton } from './FilterButton'
export default {
title: 'Button'
@@ -103,6 +104,13 @@ export const All = (): JSX.Element => {
}}>
disabled
+
+
+
diff --git a/packages/k2-alpine/src/components/Button/Button.tsx b/packages/k2-alpine/src/components/Button/Button.tsx
index d9c3f6d41f..3e30e6a8e8 100644
--- a/packages/k2-alpine/src/components/Button/Button.tsx
+++ b/packages/k2-alpine/src/components/Button/Button.tsx
@@ -20,6 +20,12 @@ export type ButtonSize = 'small' | 'medium' | 'large'
type ButtonIconType = 'check' | 'expandMore' | 'google' | 'apple'
+const BUTTON_ICON_TYPES = ['check', 'expandMore', 'google', 'apple'] as const
+
+const isButtonIconType = (value: unknown): value is ButtonIconType => {
+ return (BUTTON_ICON_TYPES as readonly string[]).includes(value as string)
+}
+
interface ButtonProps {
onPress?: () => void
disabled?: boolean
@@ -27,8 +33,8 @@ interface ButtonProps {
testID?: string
type: ButtonType
size: ButtonSize
- leftIcon?: ButtonIconType
- rightIcon?: ButtonIconType
+ leftIcon?: ButtonIconType | JSX.Element
+ rightIcon?: ButtonIconType | JSX.Element
}
export const Button = forwardRef<
@@ -104,7 +110,9 @@ export const Button = forwardRef<
alignItems: 'center',
...sizeStyles[size]
}}>
- {leftIcon
+ {React.isValidElement(leftIcon)
+ ? leftIcon
+ : isButtonIconType(leftIcon)
? getIcon(leftIcon, {
width: iconWidth,
height: iconWidth,
@@ -122,7 +130,9 @@ export const Button = forwardRef<
}}>
{children}
- {rightIcon
+ {React.isValidElement(rightIcon)
+ ? rightIcon
+ : isButtonIconType(rightIcon)
? getIcon(rightIcon, {
width: iconWidth,
height: iconWidth,
@@ -202,7 +212,7 @@ const getBackgroundColor = (
}
}
-const getTintColor = (
+export const getTintColor = (
type: ButtonType,
theme: K2AlpineTheme,
disabled: boolean | undefined
diff --git a/packages/k2-alpine/src/components/Button/FilterButton.tsx b/packages/k2-alpine/src/components/Button/FilterButton.tsx
new file mode 100644
index 0000000000..c7eb2f3ccc
--- /dev/null
+++ b/packages/k2-alpine/src/components/Button/FilterButton.tsx
@@ -0,0 +1,33 @@
+import React from 'react'
+import { ViewStyle } from 'react-native'
+import { Icons } from '../../theme/tokens/Icons'
+import { useTheme } from '../../hooks'
+import { Button, getTintColor } from './Button'
+
+export const FilterButton = ({
+ title,
+ style,
+ disabled,
+ onPress
+}: {
+ title: string
+ style?: ViewStyle
+ disabled?: boolean
+ onPress?: () => void
+}): JSX.Element => {
+ const { theme } = useTheme()
+ const tintColor = getTintColor('secondary', theme, disabled)
+
+ return (
+
+ }>
+ {title}
+
+ )
+}
diff --git a/packages/k2-alpine/src/components/Dropdown/DropdownBackground.tsx b/packages/k2-alpine/src/components/Dropdown/DropdownBackground.tsx
new file mode 100644
index 0000000000..386a259a04
--- /dev/null
+++ b/packages/k2-alpine/src/components/Dropdown/DropdownBackground.tsx
@@ -0,0 +1,35 @@
+import { BlurView } from 'expo-blur'
+import React from 'react'
+import { Platform } from 'react-native'
+import { View } from '../Primitives'
+import { useTheme } from '../../hooks'
+import { colors } from '../../theme/tokens/colors'
+
+export const DropdownBackground = ({
+ children
+}: {
+ children: React.ReactNode
+}): JSX.Element => {
+ const { theme } = useTheme()
+
+ return Platform.OS === 'ios' ? (
+
+ {children}
+
+ ) : (
+
+ {children}
+
+ )
+}
diff --git a/packages/k2-alpine/src/components/Dropdown/SimpleDropdown.stories.tsx b/packages/k2-alpine/src/components/Dropdown/SimpleDropdown.stories.tsx
index 77cb9754e2..e0bdd49415 100644
--- a/packages/k2-alpine/src/components/Dropdown/SimpleDropdown.stories.tsx
+++ b/packages/k2-alpine/src/components/Dropdown/SimpleDropdown.stories.tsx
@@ -1,8 +1,11 @@
-import React, { useState } from 'react'
+import React, { useRef, useState } from 'react'
+import { TouchableOpacity } from 'react-native'
+import { Rect } from 'react-native-popover-view'
import { ScrollView, Text, View } from '../Primitives'
import { Button } from '../Button/Button'
import { showAlert } from '../Alert/Alert'
import { IndexPath, SimpleDropdown } from './SimpleDropdown'
+import { usePopoverAnchor } from './usePopoverAnchor'
export default {
title: 'Dropdown'
@@ -20,6 +23,8 @@ export const All = (): JSX.Element => {
+
+
)
@@ -178,3 +183,89 @@ const MultipleSectionMultipleSelectionDropdown = (): JSX.Element => {
)
}
+
+const LeftAlignedDropdown = (): JSX.Element => {
+ const sections = [
+ ['All networks', 'Avalanche C-Chain', 'Bitcoin network', 'Ethereum']
+ ]
+ const [selectedRow, setSelectedRow] = useState({
+ section: 0,
+ row: 0
+ })
+ const sourceRef = useRef(null)
+
+ const { anchorRect, isPopoverVisible, onShowPopover, onHidePopover } =
+ usePopoverAnchor(sourceRef)
+
+ return (
+
+ Left aligned
+
+ setSelectedRow(indexPath)}
+ onRequestClose={onHidePopover}
+ />
+
+ )
+}
+
+const RightAlignedDropdown = (): JSX.Element => {
+ const sections = [
+ ['All networks', 'Avalanche C-Chain', 'Bitcoin network', 'Ethereum']
+ ]
+ const [selectedRow, setSelectedRow] = useState({
+ section: 0,
+ row: 0
+ })
+ const sourceRef = useRef(null)
+
+ const { anchorRect, isPopoverVisible, onShowPopover, onHidePopover } =
+ usePopoverAnchor(sourceRef)
+
+ return (
+
+ Right aligned
+
+ setSelectedRow(indexPath)}
+ onRequestClose={onHidePopover}
+ />
+
+ )
+}
diff --git a/packages/k2-alpine/src/components/Dropdown/SimpleDropdown.tsx b/packages/k2-alpine/src/components/Dropdown/SimpleDropdown.tsx
index a64686d297..8f1cb5c2c4 100644
--- a/packages/k2-alpine/src/components/Dropdown/SimpleDropdown.tsx
+++ b/packages/k2-alpine/src/components/Dropdown/SimpleDropdown.tsx
@@ -1,10 +1,11 @@
import React, { useRef } from 'react'
-import Popover from 'react-native-popover-view'
-import { BlurView } from 'expo-blur'
+import Popover, { Rect } from 'react-native-popover-view'
+import { Platform } from 'react-native'
import { Text, View, TouchableOpacity } from '../Primitives'
import { useTheme } from '../../hooks'
import { Separator } from '../Separator/Separator'
import { Icons } from '../../theme/tokens/Icons'
+import { DropdownBackground } from './DropdownBackground'
export const SimpleDropdown = ({
from,
@@ -13,15 +14,19 @@ export const SimpleDropdown = ({
offset = 0,
allowsMultipleSelection = false,
onSelectRow,
- onDeselectRow
+ onDeselectRow,
+ onRequestClose,
+ isVisible
}: {
- from: React.ReactNode
+ from: React.ReactNode | Rect
sections: T[][]
selectedRows: IndexPath[]
allowsMultipleSelection?: boolean
offset?: number
onSelectRow: (indexPath: IndexPath) => void
onDeselectRow?: (indexPath: IndexPath) => void
+ onRequestClose?: () => void
+ isVisible?: boolean
}): JSX.Element => {
const { theme } = useTheme()
const popoverRef = useRef()
@@ -48,28 +53,28 @@ export const SimpleDropdown = ({
)
}
+ const groupSeparatorHeight = Platform.OS === 'ios' ? 6 : 1
+ const backgroundBorderRadius = Platform.OS === 'ios' ? 10 : 4
+
return (
-
+
{sections.map((section, sectionIndex) => {
return (
@@ -107,14 +112,15 @@ export const SimpleDropdown = ({
)}
- {rowIndex !== section.length - 1 && }
+ {rowIndex !== section.length - 1 &&
+ Platform.OS === 'ios' && }
)
})}
{sectionIndex !== sections.length - 1 && (
@@ -122,7 +128,7 @@ export const SimpleDropdown = ({
)
})}
-
+
)
}
diff --git a/packages/k2-alpine/src/components/Dropdown/usePopoverAnchor.ts b/packages/k2-alpine/src/components/Dropdown/usePopoverAnchor.ts
new file mode 100644
index 0000000000..9548c2cb24
--- /dev/null
+++ b/packages/k2-alpine/src/components/Dropdown/usePopoverAnchor.ts
@@ -0,0 +1,43 @@
+import { useEffect, useState } from 'react'
+import { NativeMethods } from 'react-native'
+import { Rect } from 'react-native-popover-view'
+
+export const usePopoverAnchor = (
+ sourceRef: React.RefObject
+): {
+ anchorRect: Rect | undefined
+ isPopoverVisible: boolean
+ onShowPopover: () => void
+ onHidePopover: () => void
+} => {
+ const [isPopoverVisible, setIsPopoverVisible] = useState(false)
+ const [anchorRect, setAnchorRect] = useState()
+
+ const showPopover = (): void => {
+ if (sourceRef.current) {
+ // eslint-disable-next-line max-params
+ sourceRef.current.measureInWindow((x, y, width, height) => {
+ setAnchorRect(new Rect(x, y, width, height))
+ })
+ }
+ }
+
+ const hidePopover = (): void => {
+ setAnchorRect(undefined)
+ }
+
+ useEffect(() => {
+ if (anchorRect) {
+ setIsPopoverVisible(true)
+ } else {
+ setIsPopoverVisible(false)
+ }
+ }, [anchorRect])
+
+ return {
+ anchorRect,
+ isPopoverVisible,
+ onShowPopover: showPopover,
+ onHidePopover: hidePopover
+ }
+}
diff --git a/packages/k2-alpine/src/components/Header/BalanceHeader.tsx b/packages/k2-alpine/src/components/Header/BalanceHeader.tsx
index e463c30899..123621b2d6 100644
--- a/packages/k2-alpine/src/components/Header/BalanceHeader.tsx
+++ b/packages/k2-alpine/src/components/Header/BalanceHeader.tsx
@@ -29,7 +29,7 @@ export const BalanceHeader = ({
{accountName}
diff --git a/packages/k2-alpine/src/theme/tokens/Icons.ts b/packages/k2-alpine/src/theme/tokens/Icons.ts
index fd374eaf4b..f752d86236 100644
--- a/packages/k2-alpine/src/theme/tokens/Icons.ts
+++ b/packages/k2-alpine/src/theme/tokens/Icons.ts
@@ -33,6 +33,7 @@ import IconTrendingArrowDown from '../../assets/icons/trending_arrow_down.svg'
import IconQRCode2 from '../../assets/icons/qr_code_2.svg'
import IconNotifications from '../../assets/icons/notifications.svg'
import IconAlertError from '../../assets/icons/alert_error.svg'
+import IconArrowDown from '../../assets/icons/arrow_down.svg'
export const Icons = {
Action: {
@@ -74,7 +75,8 @@ export const Icons = {
Psychiatry: IconPsychiatry,
Error: IconError,
TrendingArrowUp: IconTrendingArrowUp,
- TrendingArrowDown: IconTrendingArrowDown
+ TrendingArrowDown: IconTrendingArrowDown,
+ ArrowDown: IconArrowDown
},
RecoveryMethod: {
Passkey: IconPasskey,