Skip to content

Commit 31b3f93

Browse files
committed
feat: add BadgeInfoIcon component and integrate popup alert examples in UiAndComponents
1 parent a62fb18 commit 31b3f93

File tree

5 files changed

+142
-21
lines changed

5 files changed

+142
-21
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
2+
import React from 'react'
3+
import Svg, { Circle, ClipPath, Defs, Ellipse, G, Line, LinearGradient, Mask, Path, Polygon, Polyline, RadialGradient, Rect, Stop } from 'react-native-svg'
4+
import {type Variant, type HugeIconProps, defaultStrokeWidth, defaultVariant, defaultColor, defaultSize } from './constants'
5+
6+
const iconMap: Partial<Record<Variant, React.FC<HugeIconProps>>> = {
7+
'solid-rounded': SolidRounded,
8+
'stroke-rounded': StrokeRounded,
9+
}
10+
11+
export default function BadgeInfoIcon({ variant, ...rest }: HugeIconProps) {
12+
const selectedVariant = variant || defaultVariant
13+
const Component = iconMap[selectedVariant] || iconMap[defaultVariant] || StrokeRounded
14+
return <Component {...rest} />
15+
}
16+
17+
function SolidRounded({ size = defaultSize, color = defaultColor, strokeWidth = defaultStrokeWidth, className, style }: HugeIconProps) {
18+
return (<Svg className={className} style={style} width={size} height={size} viewBox="0 0 24 24" fill="none">
19+
<Path fillRule="evenodd" clipRule="evenodd" d="M9.36526 2.22073C10.8834 0.926998 13.1167 0.926987 14.6348 2.22073L14.8809 2.42972C15.4014 2.87318 16.0489 3.14192 16.7305 3.19632L17.0528 3.22171C19.0407 3.38065 20.6195 4.95937 20.7783 6.9473L20.8037 7.26956C20.8582 7.95121 21.1268 8.5987 21.5703 9.11917L21.7793 9.36526C23.0729 10.8834 23.073 13.1167 21.7793 14.6348L21.5703 14.8809C21.1268 15.4014 20.8581 16.0488 20.8037 16.7305L20.7783 17.0528C20.6194 19.0406 19.0406 20.6194 17.0528 20.7783L16.7305 20.8037C16.0488 20.8581 15.4014 21.1268 14.8809 21.5703L14.6348 21.7793C13.1167 23.073 10.8834 23.0729 9.36526 21.7793L9.11917 21.5703C8.5987 21.1268 7.95121 20.8582 7.26956 20.8037L6.9473 20.7783C4.95937 20.6195 3.38065 19.0407 3.22171 17.0528L3.19632 16.7305C3.14192 16.0489 2.87318 15.4014 2.42972 14.8809L2.22073 14.6348C0.926987 13.1167 0.926998 10.8834 2.22073 9.36526L2.42972 9.11917C2.87326 8.59869 3.14188 7.95122 3.19632 7.26956L3.22171 6.9473C3.38053 4.95928 4.95928 3.38053 6.9473 3.22171L7.26956 3.19632C7.95122 3.14188 8.59869 2.87326 9.11917 2.42972L9.36526 2.22073ZM12 11C11.4477 11 11 11.4477 11 12V16C11.0002 16.5522 11.4479 17 12 17C12.5521 16.9999 12.9999 16.5521 13 16V12C13 11.4478 12.5522 11.0001 12 11ZM12 7.25003C11.4477 7.25003 11 7.69774 11 8.25003C11.0002 8.80216 11.4479 9.25003 12 9.25003C12.5521 9.24992 12.9998 8.80209 13 8.25003C13 7.69781 12.5522 7.25014 12 7.25003Z" fill={color}/>
20+
</Svg>
21+
)
22+
}
23+
24+
function StrokeRounded({ size = defaultSize, color = defaultColor, strokeWidth = defaultStrokeWidth, className, style }: HugeIconProps) {
25+
return (<Svg className={className} style={style} width={size} height={size} viewBox="0 0 24 24" fill="none">
26+
<Path d="M14.3942 3.00083L14.1481 2.79115C12.9103 1.73628 11.0897 1.73628 9.85189 2.79115L9.60584 3.00083C8.96518 3.54679 8.16862 3.87674 7.32956 3.9437L7.00731 3.96941C5.38613 4.09878 4.09878 5.38613 3.96941 7.00731L3.9437 7.32956C3.87674 8.16862 3.54679 8.96518 3.00083 9.60584L2.79115 9.85189C1.73628 11.0897 1.73628 12.9103 2.79115 14.1481L3.00083 14.3942C3.54679 15.0348 3.87674 15.8314 3.9437 16.6704L3.96941 16.9927C4.09878 18.6139 5.38613 19.9012 7.00731 20.0306L7.32956 20.0563C8.16862 20.1233 8.96518 20.4532 9.60584 20.9992L9.85188 21.2089C11.0897 22.2637 12.9103 22.2637 14.1481 21.2089L14.3942 20.9992C15.0348 20.4532 15.8314 20.1233 16.6704 20.0563L16.9927 20.0306C18.6139 19.9012 19.9012 18.6139 20.0306 16.9927L20.0563 16.6704C20.1233 15.8314 20.4532 15.0348 20.9992 14.3942L21.2089 14.1481C22.2637 12.9103 22.2637 11.0897 21.2089 9.85188L20.9992 9.60584C20.4532 8.96518 20.1233 8.16862 20.0563 7.32956L20.0306 7.00731C19.9012 5.38613 18.6139 4.09878 16.9927 3.96941L16.6704 3.9437C15.8314 3.87674 15.0348 3.54679 14.3942 3.00083Z" stroke={color} strokeWidth={strokeWidth} strokeLinecap="round" strokeLinejoin="round"/>
27+
<Path d="M12 16V12" stroke={color} strokeWidth={strokeWidth} strokeLinecap="round" strokeLinejoin="round"/>
28+
<Path d="M12.125 8.25H12M12.25 8.25C12.25 8.11193 12.1381 8 12 8C11.8619 8 11.75 8.11193 11.75 8.25C11.75 8.38807 11.8619 8.5 12 8.5C12.1381 8.5 12.25 8.38807 12.25 8.25Z" stroke={color} strokeWidth={strokeWidth} strokeLinecap="round" strokeLinejoin="round"/>
29+
</Svg>
30+
)
31+
}

src/components/Check.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ export type CheckIconProps = {
88
} & HugeIconProps
99
export default function Check({ checked, ...rest }: CheckIconProps) {
1010
return checked ? (
11-
<Tick01Icon {...ic} color={Colors.accent} {...rest} />
11+
<Tick01Icon strokeWidth={2} {...ic} color={Colors.accent} {...rest} />
1212
) : (
13-
<Tick01Icon {...ic} style={{ opacity: 0 }} />
13+
<Tick01Icon strokeWidth={2} {...ic} style={{ opacity: 0 }} />
1414
)
1515
}

src/components/Popup.tsx

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,27 @@
1-
import popupStore from '@/zustand/popupStore'
1+
import popupStore, { type ButtonVariant, type PopupButtonDef } from '@/zustand/popupStore'
22
import { H } from '@utils/dimensions'
33
import { Medium, SemiBold } from '@utils/fonts'
44
import React from 'react'
55
import { Modal, ScrollView, TouchableOpacity, View } from 'react-native'
6-
import { MAIN_TEXT_SIZE } from './values'
6+
import { HEADING_SIZE, MAIN_TEXT_SIZE } from './values'
77

88
type PopupT = {
99
index: number
1010
text?: string
1111
title?: string
12-
buttons: {
13-
text: string
14-
onPress?: () => void
15-
}[]
12+
buttons: PopupButtonDef[]
1613
noClose?: boolean
1714
}
1815

16+
const variantStyles: Record<ButtonVariant, { bg: string; text: string }> = {
17+
default: { bg: 'bg-(--bg) dark:bg-zinc-800', text: 'text-black dark:text-white' },
18+
primary: { bg: 'bg-blue-500', text: 'text-white' },
19+
destructive: { bg: 'bg-(--bg) dark:bg-zinc-800', text: 'text-red-500' },
20+
}
21+
1922
const Popup = React.memo<PopupT>(({ text, title, buttons, index, noClose }) => {
2023
const removePopup = popupStore((store) => store.removePopup)
24+
const isRow = buttons.length === 2
2125

2226
return (
2327
<View>
@@ -32,20 +36,24 @@ const Popup = React.memo<PopupT>(({ text, title, buttons, index, noClose }) => {
3236
}}
3337
>
3438
<View className='flex-1 items-center justify-center bg-black/40 dark:bg-black/50'>
35-
<View className='w-[85%] rounded-3xl bg-white dark:bg-zinc-900'>
36-
<View className='px-6 pt-5'>
37-
<SemiBold className='text-xl text-black dark:text-white'>{title}</SemiBold>
38-
<ScrollView style={{ maxHeight: H * 0.65, marginTop: 10 }}>
39-
<Medium className='text-sm text-black dark:text-white' style={{ fontSize: MAIN_TEXT_SIZE + 1 }}>
39+
<View className='w-[83%] rounded-4xl bg-white dark:bg-zinc-900'>
40+
<View className='px-7 pt-6'>
41+
<SemiBold className='text-black dark:text-white' style={{ fontSize: HEADING_SIZE }}>
42+
{title}
43+
</SemiBold>
44+
<ScrollView style={{ maxHeight: H * 0.65, marginTop: 8 }}>
45+
<Medium className='text-black/70 dark:text-white/70' style={{ fontSize: MAIN_TEXT_SIZE + 1 }}>
4046
{text}
4147
</Medium>
4248
</ScrollView>
4349
</View>
44-
<View className='mt-5 flex-row flex-wrap items-center justify-end px-4 pb-5'>
45-
{buttons?.map((button, i) => (
50+
<View className={`mt-8 gap-2 px-5 pb-5 ${isRow ? 'flex-row' : 'flex-col'}`}>
51+
{buttons.map((button, i) => (
4652
<PopupButton
4753
key={i}
4854
text={button.text}
55+
variant={button.variant ?? 'default'}
56+
isRow={isRow}
4957
onPress={() => {
5058
button.onPress?.()
5159
removePopup(index)
@@ -62,14 +70,22 @@ const Popup = React.memo<PopupT>(({ text, title, buttons, index, noClose }) => {
6270

6371
export default Popup
6472

65-
const PopupButton = React.memo<{ text: string; onPress?: () => void }>(({ text, onPress }) => {
73+
type PopupButtonProps = {
74+
text: string
75+
onPress?: () => void
76+
variant: ButtonVariant
77+
isRow: boolean
78+
}
79+
80+
const PopupButton = React.memo<PopupButtonProps>(({ text, onPress, variant, isRow }) => {
81+
const { bg, text: textColor } = variantStyles[variant]
6682
return (
6783
<TouchableOpacity
68-
className='min-w-20 items-center justify-center rounded-lg px-3 py-3 active:bg-black/5 dark:active:bg-white/10'
84+
className={`items-center justify-center rounded-full px-4 py-4 ${bg} ${isRow ? 'flex-1' : 'w-full'}`}
6985
onPress={onPress}
70-
activeOpacity={0.5}
86+
activeOpacity={0.75}
7187
>
72-
<SemiBold className='text-black dark:text-white' style={{ fontSize: MAIN_TEXT_SIZE }}>
88+
<SemiBold className={textColor} style={{ fontSize: MAIN_TEXT_SIZE + 1, marginBottom: 2 }}>
7389
{text}
7490
</SemiBold>
7591
</TouchableOpacity>

src/screens/Settings/UiAndComponents.tsx

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import popupStore from '@/zustand/popupStore'
12
import Button from '@components/Button'
23
import Check from '@components/Check'
34
import { Gap12 } from '@components/Gap'
@@ -11,6 +12,7 @@ import SettText from '@components/Settings/SettText'
1112
import SettWrapper from '@components/Settings/SettWrapper'
1213
import { Txt } from '@components/Text'
1314
import { Toggle } from '@components/Toggle'
15+
import BadgeInfoIcon from '@hugeicons/BadgeInfoIcon'
1416
import CpuIcon from '@hugeicons/CpuIcon'
1517
import JavaIcon from '@hugeicons/JavaIcon'
1618
import JavaScriptIcon from '@hugeicons/JavaScriptIcon'
@@ -85,6 +87,8 @@ export default function UiAndComponents({ navigation }: NavProp) {
8587
<RangeSliderMemo2 />
8688
</Gap12>
8789

90+
<PopupAlertExample />
91+
8892
<Gap12>
8993
<SettGroup title='Text Inputs'>
9094
<Input
@@ -224,3 +228,65 @@ const RangeSliderMemo2 = () => {
224228
</SettGroup>
225229
)
226230
}
231+
232+
233+
function PopupAlertExample() {
234+
const alert = popupStore((store) => store.alert)
235+
return (
236+
<Gap12>
237+
<SettGroup title='Popup Alert Example'>
238+
<SettOption
239+
title='With default OK button'
240+
onPress={() => alert('Alert Title', 'This is the alert text')}
241+
Icon={<RoundIcon Icon={BadgeInfoIcon} className='bg-slate-500' />}
242+
arrow
243+
/>
244+
<SettOption
245+
title='Custom buttons'
246+
onPress={() => alert('Alert Title', 'This is the alert text', [{ text: 'Close', variant: 'primary' }])}
247+
Icon={<RoundIcon Icon={BadgeInfoIcon} className='bg-slate-500' />}
248+
arrow
249+
/>
250+
<SettOption
251+
title='Multiple buttons and no close'
252+
onPress={() =>
253+
alert(
254+
'Alert Title',
255+
'This is the alert text',
256+
[
257+
{ text: 'Cancel', variant: 'primary' },
258+
{ text: 'Delete', variant: 'destructive' },
259+
],
260+
true,
261+
)
262+
}
263+
Icon={<RoundIcon Icon={BadgeInfoIcon} className='bg-slate-500' />}
264+
arrow
265+
/>
266+
<SettOption
267+
title='More than 2 buttons'
268+
onPress={() =>
269+
alert(
270+
'Alert Title',
271+
'This is the alert text',
272+
[
273+
{ text: 'Option 1' },
274+
{ text: 'Option 2' },
275+
{ text: 'Option 3' },
276+
{ text: 'Option 4', variant: 'primary' },
277+
{ text: 'Option 5', variant: 'destructive' },
278+
],
279+
true,
280+
)
281+
}
282+
Icon={<RoundIcon Icon={BadgeInfoIcon} className='bg-slate-500' />}
283+
arrow
284+
/>
285+
</SettGroup>
286+
<SettText>
287+
These buttons will show different types of popups. You can test the popups by clicking on the buttons. The
288+
popups will have different button configurations to show the flexibility of the popup component.
289+
</SettText>
290+
</Gap12>
291+
)
292+
}

src/zustand/popupStore.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
11
import { create } from 'zustand'
22

3+
export type ButtonVariant = 'default' | 'primary' | 'destructive'
4+
5+
export type PopupButtonDef = {
6+
text: string
7+
onPress?: () => void
8+
variant?: ButtonVariant
9+
}
10+
311
export type Popup = {
412
text?: string
513
title?: string
6-
buttons?: { text: string; onPress?: () => void }[]
14+
buttons?: PopupButtonDef[]
715
noClose?: boolean
816
}
917

1018
type PopupStore = {
1119
popups: Popup[]
12-
alert: (title: string, text: string, buttons?: { text: string; onPress?: () => void }[], noClose?: boolean) => void
20+
alert: (title: string, text: string, buttons?: PopupButtonDef[], noClose?: boolean) => void
1321
removePopup: (index: number) => void
1422
clear: () => void
1523
}

0 commit comments

Comments
 (0)