mirror of
https://github.com/DocNR/POWR.git
synced 2025-04-23 01:01:27 +00:00
android ui bugs
This commit is contained in:
parent
43df1aeb79
commit
ea5dde32f4
@ -14,6 +14,7 @@ import { FilterSheet, type FilterOptions, type SourceType } from '@/components/l
|
|||||||
import { useWorkoutStore } from '@/stores/workoutStore';
|
import { useWorkoutStore } from '@/stores/workoutStore';
|
||||||
import { generateId } from '@/utils/ids';
|
import { generateId } from '@/utils/ids';
|
||||||
import { useNDKStore } from '@/lib/stores/ndk';
|
import { useNDKStore } from '@/lib/stores/ndk';
|
||||||
|
import { useIconColor } from '@/lib/theme/iconUtils';
|
||||||
|
|
||||||
// Default available filters
|
// Default available filters
|
||||||
const availableFilters = {
|
const availableFilters = {
|
||||||
@ -35,7 +36,8 @@ export default function ExercisesScreen() {
|
|||||||
const [filterSheetOpen, setFilterSheetOpen] = useState(false);
|
const [filterSheetOpen, setFilterSheetOpen] = useState(false);
|
||||||
const [currentFilters, setCurrentFilters] = useState<FilterOptions>(initialFilters);
|
const [currentFilters, setCurrentFilters] = useState<FilterOptions>(initialFilters);
|
||||||
const [activeFilters, setActiveFilters] = useState(0);
|
const [activeFilters, setActiveFilters] = useState(0);
|
||||||
|
const { getIconProps } = useIconColor();
|
||||||
|
|
||||||
// Exercise sheet state
|
// Exercise sheet state
|
||||||
const [showExerciseSheet, setShowExerciseSheet] = useState(false);
|
const [showExerciseSheet, setShowExerciseSheet] = useState(false);
|
||||||
const [exerciseToEdit, setExerciseToEdit] = useState<ExerciseDisplay | undefined>(undefined);
|
const [exerciseToEdit, setExerciseToEdit] = useState<ExerciseDisplay | undefined>(undefined);
|
||||||
@ -200,36 +202,37 @@ export default function ExercisesScreen() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View className="flex-1 bg-background">
|
<View className="flex-1 bg-background">
|
||||||
{/* Search bar with filter button */}
|
{/* Search bar with filter button */}
|
||||||
<View className="px-4 py-2 border-b border-border">
|
<View className="px-4 py-2 border-b border-border">
|
||||||
<View className="flex-row items-center">
|
<View className="flex-row items-center">
|
||||||
<View className="relative flex-1">
|
<View className="relative flex-1">
|
||||||
<View className="absolute left-3 z-10 h-full justify-center">
|
<View className="absolute left-3 z-10 h-full justify-center">
|
||||||
<Search size={18} className="text-muted-foreground" />
|
<Search size={18} {...getIconProps('primary')} />
|
||||||
</View>
|
|
||||||
<Input
|
|
||||||
value={searchQuery}
|
|
||||||
onChangeText={setSearchQuery}
|
|
||||||
placeholder="Search exercises"
|
|
||||||
className="pl-9 pr-10 bg-muted/50 border-0"
|
|
||||||
/>
|
|
||||||
<View className="absolute right-2 z-10 h-full justify-center">
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
onPress={() => setFilterSheetOpen(true)}
|
|
||||||
>
|
|
||||||
<View className="relative">
|
|
||||||
<ListFilter className="text-muted-foreground" size={20} />
|
|
||||||
{activeFilters > 0 && (
|
|
||||||
<View className="absolute -top-1 -right-1 w-2.5 h-2.5 rounded-full" style={{ backgroundColor: '#f7931a' }} />
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
</Button>
|
<Input
|
||||||
|
value={searchQuery}
|
||||||
|
onChangeText={setSearchQuery}
|
||||||
|
placeholder="Search exercises"
|
||||||
|
className="pl-9 pr-10 border-0 bg-background"
|
||||||
|
/>
|
||||||
|
<View className="absolute right-2 z-10 h-full justify-center">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onPress={() => setFilterSheetOpen(true)}
|
||||||
|
>
|
||||||
|
<View className="relative">
|
||||||
|
<ListFilter size={20} {...getIconProps('primary')} />
|
||||||
|
{activeFilters > 0 && (
|
||||||
|
<View className="absolute -top-1 -right-1 w-2.5 h-2.5 rounded-full" style={{ backgroundColor: '#f7931a' }} />
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
);
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* Filter Sheet */}
|
{/* Filter Sheet */}
|
||||||
<FilterSheet
|
<FilterSheet
|
||||||
|
@ -1,47 +0,0 @@
|
|||||||
// components/library/SearchPopover.tsx
|
|
||||||
import React from 'react';
|
|
||||||
import { View } from 'react-native';
|
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
|
||||||
import { Input } from '@/components/ui/input';
|
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import { Search, X } from 'lucide-react-native';
|
|
||||||
|
|
||||||
interface SearchPopoverProps {
|
|
||||||
searchQuery: string;
|
|
||||||
onSearchChange: (query: string) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SearchPopover({ searchQuery, onSearchChange }: SearchPopoverProps) {
|
|
||||||
const [isOpen, setIsOpen] = React.useState(false);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Popover onOpenChange={setIsOpen}>
|
|
||||||
<PopoverTrigger asChild>
|
|
||||||
<Button variant="ghost" size="icon">
|
|
||||||
<Search className="text-foreground" />
|
|
||||||
</Button>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent className="w-80 p-0" align="end">
|
|
||||||
<View className="flex-row items-center p-2 gap-2">
|
|
||||||
<Input
|
|
||||||
placeholder="Search exercises..."
|
|
||||||
value={searchQuery}
|
|
||||||
onChangeText={onSearchChange}
|
|
||||||
className="flex-1"
|
|
||||||
autoFocus
|
|
||||||
placeholderTextColor="text-muted-foreground"
|
|
||||||
/>
|
|
||||||
{searchQuery.length > 0 && (
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
onPress={() => onSearchChange('')}
|
|
||||||
>
|
|
||||||
<X size={20} className="text-muted-foreground" />
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
);
|
|
||||||
}
|
|
134
docs/design/styling_design_doc.md
Normal file
134
docs/design/styling_design_doc.md
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
# POWR App Styling Guide
|
||||||
|
|
||||||
|
This document outlines how to consistently style components in the POWR fitness app.
|
||||||
|
|
||||||
|
## Color System
|
||||||
|
|
||||||
|
All colors are defined in the theme system and should be accessed through it rather than hardcoded values.
|
||||||
|
|
||||||
|
### Import Path
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { useIconColor } from '@/lib/theme/iconUtils';
|
||||||
|
import { FIXED_COLORS } from '@/lib/theme/colors';
|
||||||
|
Icon Styling
|
||||||
|
Icons must use the icon utility to ensure visibility on both iOS and Android:
|
||||||
|
typescriptCopy// Import icon utility
|
||||||
|
import { useIconColor } from '@/lib/theme/iconUtils';
|
||||||
|
|
||||||
|
// Inside your component
|
||||||
|
const { getIconProps, getIconColor } = useIconColor();
|
||||||
|
|
||||||
|
// Apply to icons
|
||||||
|
<Icon
|
||||||
|
size={24}
|
||||||
|
{...getIconProps('primary')} // Use appropriate variant
|
||||||
|
/>
|
||||||
|
Icon Variants
|
||||||
|
|
||||||
|
primary - For main actions and interactive elements (purple)
|
||||||
|
muted - For secondary or less important actions (gray)
|
||||||
|
destructive - For delete/remove actions (red)
|
||||||
|
success - For confirmation/complete actions (green)
|
||||||
|
warning - For caution indicators (yellow/orange)
|
||||||
|
|
||||||
|
Examples
|
||||||
|
tsxCopy// Primary action icon
|
||||||
|
<Play {...getIconProps('primary')} size={20} />
|
||||||
|
|
||||||
|
// Destructive action icon
|
||||||
|
<Trash2 {...getIconProps('destructive')} size={20} />
|
||||||
|
|
||||||
|
// Icon with custom fill
|
||||||
|
<Star
|
||||||
|
{...getIconProps(isFavorite ? 'primary' : 'muted')}
|
||||||
|
fill={isFavorite ? getIconColor('primary') : "none"}
|
||||||
|
size={20}
|
||||||
|
/>
|
||||||
|
Button Styling
|
||||||
|
Use the standard button component with appropriate variants:
|
||||||
|
tsxCopy// Primary button
|
||||||
|
<Button variant="default" className="w-full">
|
||||||
|
<Text className="text-primary-foreground">Primary Action</Text>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
// Destructive button
|
||||||
|
<Button variant="destructive" className="w-full">
|
||||||
|
<Text className="text-destructive-foreground">Delete</Text>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
// Outline button
|
||||||
|
<Button variant="outline" className="w-full">
|
||||||
|
<Text>Secondary Action</Text>
|
||||||
|
</Button>
|
||||||
|
Header Component
|
||||||
|
Use the Header component consistently:
|
||||||
|
tsxCopy// Standard header with title
|
||||||
|
<Header title="Screen Title" showNotifications={true} />
|
||||||
|
|
||||||
|
// Header with logo
|
||||||
|
<Header useLogo={true} showNotifications={true} />
|
||||||
|
|
||||||
|
// Header with custom right element
|
||||||
|
<Header
|
||||||
|
title="Screen Title"
|
||||||
|
rightElement={<YourCustomElement />}
|
||||||
|
/>
|
||||||
|
Text Styling
|
||||||
|
Use the Text component with appropriate Tailwind classes:
|
||||||
|
tsxCopy// Headings
|
||||||
|
<Text className="text-xl font-semibold text-foreground">Heading</Text>
|
||||||
|
|
||||||
|
// Body text
|
||||||
|
<Text className="text-base text-foreground">Regular text</Text>
|
||||||
|
|
||||||
|
// Secondary text
|
||||||
|
<Text className="text-sm text-muted-foreground">Secondary text</Text>
|
||||||
|
Card Components
|
||||||
|
Use the Card component for content blocks:
|
||||||
|
tsxCopy<Card className="mx-4">
|
||||||
|
<CardContent className="p-4">
|
||||||
|
{/* Card content */}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
Dialog/Alert Styling
|
||||||
|
Center buttons in dialogs:
|
||||||
|
tsxCopy<AlertDialog>
|
||||||
|
<AlertDialogContent>
|
||||||
|
<AlertDialogHeader>
|
||||||
|
<AlertDialogTitle>
|
||||||
|
<Text>Alert Title</Text>
|
||||||
|
</AlertDialogTitle>
|
||||||
|
<AlertDialogDescription>
|
||||||
|
<Text>Alert description text.</Text>
|
||||||
|
</AlertDialogDescription>
|
||||||
|
</AlertDialogHeader>
|
||||||
|
<View className="flex-row justify-center gap-3">
|
||||||
|
<AlertDialogCancel>
|
||||||
|
<Text>Cancel</Text>
|
||||||
|
</AlertDialogCancel>
|
||||||
|
<AlertDialogAction className="bg-destructive">
|
||||||
|
<Text className="text-destructive-foreground">Confirm</Text>
|
||||||
|
</AlertDialogAction>
|
||||||
|
</View>
|
||||||
|
</AlertDialogContent>
|
||||||
|
</AlertDialog>
|
||||||
|
Best Practices
|
||||||
|
|
||||||
|
Never use hardcoded colors - Always use theme variables through Tailwind classes
|
||||||
|
Always use getIconProps for icons - Ensures visibility on both iOS and Android
|
||||||
|
Use semantic variants - Choose button and icon variants based on their purpose
|
||||||
|
Maintain consistent spacing - Use Tailwind spacing classes (p-4, m-2, etc.)
|
||||||
|
Check both platforms - Test UI changes on both iOS and Android
|
||||||
|
|
||||||
|
Troubleshooting
|
||||||
|
If icons aren't appearing on Android:
|
||||||
|
|
||||||
|
Ensure you're using getIconProps() instead of className for the icon
|
||||||
|
Add strokeWidth={2} for better visibility on Android
|
||||||
|
Check that the icon has a size specified
|
||||||
|
|
||||||
|
If colors seem inconsistent:
|
||||||
|
|
||||||
|
Verify you're using Tailwind classes (text-primary vs #8B5CF6)
|
||||||
|
Check that the correct variant is being used for your component
|
Loading…
x
Reference in New Issue
Block a user