diff --git a/frontend/README.md b/frontend/README.md index 1a4e707..15e2506 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,4 +1,4 @@ -# Welcome to your Expo app 👋 +# Longhorn Studies This is an [Expo](https://expo.dev) project created with [`create-expo-app`](https://www.npmjs.com/package/create-expo-app). diff --git a/frontend/app/(tabs)/_layout.tsx b/frontend/app/(tabs)/_layout.tsx index 54e11d0..97dab64 100644 --- a/frontend/app/(tabs)/_layout.tsx +++ b/frontend/app/(tabs)/_layout.tsx @@ -15,7 +15,9 @@ export default function TabLayout() { tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint, headerShown: false, tabBarButton: HapticTab, - }}> + }} + + > - }> + } + > Welcome! @@ -74,11 +78,6 @@ export default function HomeScreen() { app-example. - - - Welcome to Nativewind! - - ); } diff --git a/frontend/app/(tabs)/test.tsx b/frontend/app/(tabs)/test.tsx new file mode 100644 index 0000000..3976955 --- /dev/null +++ b/frontend/app/(tabs)/test.tsx @@ -0,0 +1,21 @@ +import { Image } from "expo-image"; +import { View } from "react-native"; + +import StudySpotCard from "@/components/ui/study-spot-card"; + +export default function HomeScreen() { + return ( + + + + + + + ); +} diff --git a/frontend/assets/fonts/Roboto-Flex.ttf b/frontend/assets/fonts/Roboto-Flex.ttf new file mode 100644 index 0000000..2e5c2a2 Binary files /dev/null and b/frontend/assets/fonts/Roboto-Flex.ttf differ diff --git a/frontend/components/ui/icons/bookmark.tsx b/frontend/components/ui/icons/bookmark.tsx new file mode 100644 index 0000000..af8a3df --- /dev/null +++ b/frontend/components/ui/icons/bookmark.tsx @@ -0,0 +1,28 @@ +import React from "react"; +import Svg, { Path } from "react-native-svg"; + +import type { IconProps } from "."; + +import { cssInterop } from "nativewind"; + +// 1. Tell NativeWind to intercept the `className` prop and apply it as a style to the Svg +cssInterop(Svg, { + className: { + target: "style", + }, +}); + +export default function BookmarkIcon({ + color = "#1A2024", + size = 24, + ...props +}: IconProps) { + return ( + + + + ); +} \ No newline at end of file diff --git a/frontend/components/ui/icons/caret-right.tsx b/frontend/components/ui/icons/caret-right.tsx new file mode 100644 index 0000000..eb2690f --- /dev/null +++ b/frontend/components/ui/icons/caret-right.tsx @@ -0,0 +1,28 @@ +import React from "react"; +import Svg, { Path } from "react-native-svg"; + +import type { IconProps } from "."; + +import { cssInterop } from "nativewind"; + +// 1. Tell NativeWind to intercept the `className` prop and apply it as a style to the Svg +cssInterop(Svg, { + className: { + target: "style", + }, +}); + +export default function CaretRightIcon({ + color = "#1A2024", + size = 24, + ...props +}: IconProps) { + return ( + + + + ); +} \ No newline at end of file diff --git a/frontend/components/ui/icons/clock.tsx b/frontend/components/ui/icons/clock.tsx new file mode 100644 index 0000000..a391d72 --- /dev/null +++ b/frontend/components/ui/icons/clock.tsx @@ -0,0 +1,28 @@ +import React from "react"; +import Svg, { Path } from "react-native-svg"; + +import type { IconProps } from "."; + +import { cssInterop } from "nativewind"; + +// 1. Tell NativeWind to intercept the `className` prop and apply it as a style to the Svg +cssInterop(Svg, { + className: { + target: "style", + }, +}); + +export default function ClockIcon({ + color = "#1A2024", + size = 24, + ...props +}: IconProps) { + return ( + + + + ); +} diff --git a/frontend/components/ui/icons/index.tsx b/frontend/components/ui/icons/index.tsx new file mode 100644 index 0000000..bc0dc84 --- /dev/null +++ b/frontend/components/ui/icons/index.tsx @@ -0,0 +1,12 @@ +import { SvgProps } from "react-native-svg"; + +import ClockIcon from "./clock"; +import BookmarkIcon from "./bookmark"; +import CaretRightIcon from "./caret-right"; +import WalkingIcon from "./walking"; +export interface IconProps extends SvgProps { + color?: string; + size?: number; +} + +export { ClockIcon, BookmarkIcon, CaretRightIcon, WalkingIcon }; diff --git a/frontend/components/ui/icons/walking.tsx b/frontend/components/ui/icons/walking.tsx new file mode 100644 index 0000000..b7093c3 --- /dev/null +++ b/frontend/components/ui/icons/walking.tsx @@ -0,0 +1,28 @@ +import React from "react"; +import Svg, { Path } from "react-native-svg"; + +import type { IconProps } from "."; + +import { cssInterop } from "nativewind"; + +// 1. Tell NativeWind to intercept the `className` prop and apply it as a style to the Svg +cssInterop(Svg, { + className: { + target: "style", + }, +}); + +export default function WalkingIcon({ + color = "#94A3B8", + size = 14, + ...props +}: IconProps) { + return ( + + + + ); +} \ No newline at end of file diff --git a/frontend/components/ui/open-status.tsx b/frontend/components/ui/open-status.tsx new file mode 100644 index 0000000..d36dca9 --- /dev/null +++ b/frontend/components/ui/open-status.tsx @@ -0,0 +1,29 @@ +import { View, Text } from "react-native"; +import { ClockIcon } from "./icons"; + +type OpenStatusType = { + openStatus: "open" | "closing" | "closed"; + hours?: string[]; +}; + +export default function OpenStatus({ openStatus, hours }: OpenStatusType) { + const color = `text-status-${openStatus}`; + return ( + + {/** ENSURES COLORS LOAD PROPERLY */} + + + {openStatus === "open" && "Open"} + {openStatus === "closing" && "Closing"} + {openStatus === "closed" && "Closed"} + + {hours && openStatus !== "closed" && until {hours[1]}} + {hours && openStatus === "closed" && until {hours[0]}} + + ); +} diff --git a/frontend/components/ui/study-spot-card/index.tsx b/frontend/components/ui/study-spot-card/index.tsx new file mode 100644 index 0000000..b6147d4 --- /dev/null +++ b/frontend/components/ui/study-spot-card/index.tsx @@ -0,0 +1,71 @@ +import { Image } from "expo-image"; + +import { View, Text, TouchableOpacity } from "react-native"; + +import OpenStatus from "../open-status"; +import { Tag } from "../tags"; + +import { BookmarkIcon, CaretRightIcon, WalkingIcon } from "../icons"; + +const blurhash = + "|rF?hV%2WCj[ayj[a|j[az_NaeWBj@ayfRayfQfQM{M|azj[azf6fQfQfQIpWXofj[ayj[j[fQayWCoeoeaya}j[ayfQa{oLj?j[WVj[ayayj[fQoff7azayj[ayj[j[ayofayayayj[fQj[ayayj[ayfjj[j[ayjuayj["; + +type StudySpotCardType = { + locationName: string; + buildingName: string; + location: string; // TODO: get proper type + hours: string[]; + tags: string[]; +}; +export default function StudySpotCard({ + locationName, + buildingName, + hours, + tags, +}: StudySpotCardType) { + return ( + + + + console.log("Bookmark tapped!")} + hitSlop={{ top: 15, bottom: 15, left: 15, right: 15 }} + > + + + + + + + + {buildingName} + + {locationName} + + + + + 0.1 mi + + + + + + + + + + + + + + + ); +} diff --git a/frontend/components/ui/tags/index.tsx b/frontend/components/ui/tags/index.tsx new file mode 100644 index 0000000..c56afb1 --- /dev/null +++ b/frontend/components/ui/tags/index.tsx @@ -0,0 +1,13 @@ +import { View, Text } from "react-native"; + +type TagProps = { + tag: string; +}; + +export function Tag({ tag }: TagProps) { + return ( + + {tag} + + ); +} diff --git a/frontend/package.json b/frontend/package.json index 62314bc..5aea7d9 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -34,8 +34,9 @@ "react-native": "0.81.5", "react-native-gesture-handler": "~2.28.0", "react-native-reanimated": "^4.1.1", - "react-native-safe-area-context": "^5.4.0", + "react-native-safe-area-context": "^5.6.0", "react-native-screens": "~4.16.0", + "react-native-svg": "15.12.1", "react-native-web": "~0.21.0", "react-native-worklets": "0.5.1", "tailwindcss": "^3.4.19" diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index e4fb158..1c71e6b 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -2,7 +2,38 @@ module.exports = { content: ['./components/**/*.{js,jsx,ts,tsx}', './app/**/*.{js,jsx,ts,tsx}'], theme: { - extend: {}, + extend: { + colors: { + // Primary + 'white': '#FFFFFF', + 'burnt-orange': '#BF5700', // study spot building names/abbrevs + 'main-text': '#333F48', // Used with all main texts + 'card-border': '#BAC1CC', // All card borders + 'gray-text': '#94A3B8', // Gray text + + // Opaque colors + // Note: You can also use Tailwind's built-in opacity modifier like `bg-brand-orange/50` + 'active-filter': 'rgba(191, 87, 0, 0.5)', // #BF5700 at 0.5 opacity + 'saved-bg': 'rgba(214, 210, 196, 0.5)', // #D6D2C4 at 0.5 opacity + + // Hours Color States + 'status-open': '#579D42', + 'status-closing': '#F8971F', + 'status-closed': '#D10000', + }, + fontFamily: { + roboto: ['"Roboto Flex"', 'sans-serif'], + }, + fontSize: { + 'heading': '25.63px', // Heading + 'details-header': '22.78px', // Study Spot Details Header + 'filter-header': '20.25px', // Filter Header + 'base-16': '16px', // Filter Option, Details, Search Bar + 'spot-name': '14.22px', // Study Spot Name, Details + 'status': '12.64px', // Open Status + Distance + 'tag': '10px', // Tags + } + }, }, presets: [require('nativewind/preset')], plugins: [],