Peace 2 weeks ago
parent cd5b8db8db
commit 2ba5602b27
  1. 4
      lite.todo/app/(tabs)/_layout.tsx
  2. 28
      lite.todo/app/(tabs)/done.tsx
  3. 37
      lite.todo/app/(tabs)/index.tsx
  4. 2
      lite.todo/app/_layout.tsx
  5. 23
      lite.todo/components/TodoList.tsx
  6. 1600
      lite.todo/components/ui/icon/index.tsx
  7. 1573
      lite.todo/components/ui/icon/index.web.tsx
  8. 26
      lite.todo/package-lock.json
  9. 14
      lite.todo/package.json
  10. 23
      lite.todo/store/todoProvider.tsx

@ -1,13 +1,16 @@
import { Tabs } from "expo-router"; import { Tabs } from "expo-router";
import { MaterialIcons } from "@expo/vector-icons"; import { MaterialIcons } from "@expo/vector-icons";
import { TodosProvider } from "@/store/todoProvider";
export default function TabLayout() { export default function TabLayout() {
return ( return (
<TodosProvider>
<Tabs <Tabs
screenOptions={{ screenOptions={{
headerShown: false, headerShown: false,
tabBarStyle: { height: 56 }, tabBarStyle: { height: 56 },
tabBarLabelStyle: { fontSize: 12 }, tabBarLabelStyle: { fontSize: 12 },
tabBarHideOnKeyboard: true,
}} }}
> >
<Tabs.Screen <Tabs.Screen
@ -37,5 +40,6 @@ export default function TabLayout() {
}} }}
/> />
</Tabs> </Tabs>
</TodosProvider>
); );
} }

@ -1,5 +1,31 @@
import TodoList from "@/components/TodoList";
import { Button, ButtonText } from "@/components/ui/button";
import { GS } from "@/components/ui/gluestack-ui-provider/gluestack-ui-token";
import { HStack } from "@/components/ui/hstack";
import { Text } from "@/components/ui/text";
import { VStack } from "@/components/ui/vstack";
import { useTodosCtx } from "@/store/todoProvider";
import { SafeAreaView } from "react-native-safe-area-context"; import { SafeAreaView } from "react-native-safe-area-context";
export default function DoneTab() { export default function DoneTab() {
return <SafeAreaView className="flex-1"></SafeAreaView>; const { doneTodos, toggle, remove, clearDone } = useTodosCtx();
return (
<SafeAreaView className="flex-1 bg-white dark:bg-neutral-900">
<VStack className="flex-1 px-4 py-1 gap-3">
<Text className="text-2xl font-bold"> </Text>
<TodoList data={doneTodos} onToggle={toggle} onRemove={remove} />
<HStack className="justify-end">
<Button
className="flex-1"
variant={GS.variant.outline}
action={GS.action.secondary}
onPress={clearDone}
>
<ButtonText> </ButtonText>
</Button>
</HStack>
</VStack>
</SafeAreaView>
);
} }

@ -1,11 +1,17 @@
import TodoList from "@/components/TodoList";
import { Button, ButtonText } from "@/components/ui/button";
import { GS } from "@/components/ui/gluestack-ui-provider/gluestack-ui-token";
import { HStack } from "@/components/ui/hstack";
import { Input, InputField } from "@/components/ui/input";
import { Text } from "@/components/ui/text"; import { Text } from "@/components/ui/text";
import { VStack } from "@/components/ui/vstack"; import { VStack } from "@/components/ui/vstack";
import { useTodos } from "@/store/useTodos"; import { useTodosCtx } from "@/store/todoProvider";
import { useState } from "react"; import { useState } from "react";
import { KeyboardAvoidingView, Platform } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context"; import { SafeAreaView } from "react-native-safe-area-context";
export default function ActiveTab() { export default function ActiveTab() {
const { activeTodos, leftCount, add, toggle, completeAll } = useTodos(); const { activeTodos, leftCount, add, toggle, completeAll } = useTodosCtx();
const [title, setTitle] = useState(""); const [title, setTitle] = useState("");
const onAdd = () => { const onAdd = () => {
@ -14,10 +20,33 @@ export default function ActiveTab() {
}; };
return ( return (
<SafeAreaView className="flex-1"> <SafeAreaView style={{ flex: 1 }} className="bg-white dark:bg-neutral-900">
<VStack className="flex-1 p-4 gap-3"> <KeyboardAvoidingView
style={{ flex: 1 }}
behavior={Platform.OS === "ios" ? "padding" : "height"}
keyboardVerticalOffset={30}
>
<VStack className="flex-1 px-4 py-1 gap-3">
<Text className="text-2xl font-bold"></Text> <Text className="text-2xl font-bold"></Text>
{/* 확인 창 */}
<TodoList data={activeTodos} onToggle={toggle} />
{/* 입력 창 */}
<HStack className="gap-2">
<Input className="flex-1">
<InputField
value={title}
onChangeText={setTitle}
placeholder="할 일을 입력하세요"
returnKeyType="done"
onSubmitEditing={onAdd}
/>
</Input>
<Button action={GS.action.primary} onPress={onAdd}>
<ButtonText></ButtonText>
</Button>
</HStack>
</VStack> </VStack>
</KeyboardAvoidingView>
</SafeAreaView> </SafeAreaView>
); );
} }

@ -11,7 +11,7 @@ export default function App() {
<GluestackUIProvider mode="light"> <GluestackUIProvider mode="light">
<SafeAreaView style={{ flex: 1 }}> <SafeAreaView style={{ flex: 1 }}>
<StatusBar style="auto" /> <StatusBar style="auto" />
<Stack screenOptions={{ headerShown: false }} /> <Stack screenOptions={{ header: () => null }} />
</SafeAreaView> </SafeAreaView>
</GluestackUIProvider> </GluestackUIProvider>
); );

@ -11,6 +11,7 @@ import {
} from "./ui/checkbox"; } from "./ui/checkbox";
import { Button, ButtonText } from "./ui/button"; import { Button, ButtonText } from "./ui/button";
import { GS } from "./ui/gluestack-ui-provider/gluestack-ui-token"; import { GS } from "./ui/gluestack-ui-provider/gluestack-ui-token";
import { CheckIcon, CloseIcon, Icon } from "@/components/ui/icon";
type Props = { type Props = {
data: Todo[]; data: Todo[];
@ -24,15 +25,16 @@ export default function TodoList({ data, onToggle, onRemove }: Props) {
data={data} data={data}
keyExtractor={(it) => it.id} keyExtractor={(it) => it.id}
contentContainerStyle={{ paddingBottom: 80 }} contentContainerStyle={{ paddingBottom: 80 }}
renderItem={({ item }) => ( renderItem={({ item }) => {
<HStack className="items-center py-2"> return (
<HStack className="items-center justify-between py-2 hover:bg-secondary-200">
<Checkbox <Checkbox
isChecked={item.done} isChecked={item.done}
onChange={(_isChecked) => onToggle(item.id)} onChange={(_isChecked) => onToggle(item.id)}
value={item.title} value={item.title}
> >
<CheckboxIndicator> <CheckboxIndicator>
<CheckboxIcon /> <CheckboxIcon as={CheckIcon} />
</CheckboxIndicator> </CheckboxIndicator>
<CheckboxLabel className="data-[checked=true]:line-through"> <CheckboxLabel className="data-[checked=true]:line-through">
{item.title} {item.title}
@ -40,15 +42,24 @@ export default function TodoList({ data, onToggle, onRemove }: Props) {
</Checkbox> </Checkbox>
{onRemove ? ( {onRemove ? (
<Button <Button
className="pl-2"
variant={GS.variant.link} variant={GS.variant.link}
action={GS.action.negative} action={GS.action.secondary}
onPress={() => onRemove(item.id)} onPress={() => onRemove(item.id)}
> >
<ButtonText></ButtonText> <HStack className="flex justify-center items-center">
<Icon
as={CloseIcon}
size={GS.size.xs}
className="text-secondary-950"
/>
<ButtonText size={GS.size.xs}></ButtonText>
</HStack>
</Button> </Button>
) : null} ) : null}
</HStack> </HStack>
)} );
}}
ListEmptyComponent={ ListEmptyComponent={
<Text className="text-neutral-400 mt-6 text-center"> <Text className="text-neutral-400 mt-6 text-center">
. .

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -29,7 +29,7 @@
"react-native-reanimated": "^4.1.0", "react-native-reanimated": "^4.1.0",
"react-native-safe-area-context": "5.4.0", "react-native-safe-area-context": "5.4.0",
"react-native-screens": "~4.11.1", "react-native-screens": "~4.11.1",
"react-native-svg": "^15.2.0", "react-native-svg": "^15.12.0",
"react-native-worklets": "^0.5.0", "react-native-worklets": "^0.5.0",
"react-stately": "^3.39.0", "react-stately": "^3.39.0",
"tailwind-variants": "^0.1.20", "tailwind-variants": "^0.1.20",
@ -2096,21 +2096,6 @@
"react-native-web": ">=0.19.0" "react-native-web": ">=0.19.0"
} }
}, },
"node_modules/@gluestack-ui/core/node_modules/react-native-svg": {
"version": "15.12.1",
"resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.12.1.tgz",
"integrity": "sha512-vCuZJDf8a5aNC2dlMovEv4Z0jjEUET53lm/iILFnFewa15b4atjVxU6Wirm6O9y6dEsdjDZVD7Q3QM4T1wlI8g==",
"license": "MIT",
"dependencies": {
"css-select": "^5.1.0",
"css-tree": "^1.1.3",
"warn-once": "0.1.1"
},
"peerDependencies": {
"react": "*",
"react-native": "*"
}
},
"node_modules/@gluestack-ui/utils": { "node_modules/@gluestack-ui/utils": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/@gluestack-ui/utils/-/utils-3.0.0.tgz", "resolved": "https://registry.npmjs.org/@gluestack-ui/utils/-/utils-3.0.0.tgz",
@ -9750,13 +9735,14 @@
} }
}, },
"node_modules/react-native-svg": { "node_modules/react-native-svg": {
"version": "15.2.0", "version": "15.12.0",
"resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.2.0.tgz", "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.12.0.tgz",
"integrity": "sha512-R0E6IhcJfVLsL0lRmnUSm72QO+mTqcAOM5Jb8FVGxJqX3NfJMlMP0YyvcajZiaRR8CqQUpEoqrY25eyZb006kw==", "integrity": "sha512-iE25PxIJ6V0C6krReLquVw6R0QTsRTmEQc4K2Co3P6zsimU/jltcDBKYDy1h/5j9S/fqmMeXnpM+9LEWKJKI6A==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"css-select": "^5.1.0", "css-select": "^5.1.0",
"css-tree": "^1.1.3" "css-tree": "^1.1.3",
"warn-once": "0.1.1"
}, },
"peerDependencies": { "peerDependencies": {
"react": "*", "react": "*",

@ -10,12 +10,16 @@
}, },
"dependencies": { "dependencies": {
"@expo/html-elements": "^0.10.1", "@expo/html-elements": "^0.10.1",
"@expo/vector-icons": "^14.1.0",
"@gluestack-ui/core": "^3.0.0", "@gluestack-ui/core": "^3.0.0",
"@gluestack-ui/utils": "^3.0.0", "@gluestack-ui/utils": "^3.0.0",
"@legendapp/motion": "^2.3.0", "@legendapp/motion": "^2.3.0",
"@react-native-async-storage/async-storage": "^2.2.0", "@react-native-async-storage/async-storage": "^2.2.0",
"babel-plugin-module-resolver": "^5.0.2", "babel-plugin-module-resolver": "^5.0.2",
"expo": "~53.0.22", "expo": "~53.0.22",
"expo-constants": "~17.1.7",
"expo-linking": "~7.1.7",
"expo-router": "~5.1.5",
"expo-status-bar": "~2.2.3", "expo-status-bar": "~2.2.3",
"lucide-react-native": "^0.510.0", "lucide-react-native": "^0.510.0",
"nativewind": "^4.1.23", "nativewind": "^4.1.23",
@ -25,16 +29,12 @@
"react-native": "0.79.6", "react-native": "0.79.6",
"react-native-reanimated": "^4.1.0", "react-native-reanimated": "^4.1.0",
"react-native-safe-area-context": "5.4.0", "react-native-safe-area-context": "5.4.0",
"react-native-svg": "^15.2.0", "react-native-screens": "~4.11.1",
"react-native-svg": "^15.12.0",
"react-native-worklets": "^0.5.0", "react-native-worklets": "^0.5.0",
"react-stately": "^3.39.0", "react-stately": "^3.39.0",
"tailwind-variants": "^0.1.20", "tailwind-variants": "^0.1.20",
"tailwindcss": "^3.4.17", "tailwindcss": "^3.4.17"
"expo-router": "~5.1.5",
"react-native-screens": "~4.11.1",
"expo-linking": "~7.1.7",
"expo-constants": "~17.1.7",
"@expo/vector-icons": "^14.1.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.25.2", "@babel/core": "^7.25.2",

@ -0,0 +1,23 @@
import { createContext, useContext } from "react";
import { type Todo, useTodos } from "./useTodos";
type TodosContextValue = ReturnType<typeof useTodos>;
const TodosContext = createContext<TodosContextValue | null>(null);
export function TodosProvider({ children }: { children: React.ReactNode }) {
const store = useTodos();
return (
<TodosContext.Provider value={store}>{children}</TodosContext.Provider>
);
}
export function useTodosCtx(): TodosContextValue {
const ctx = useContext(TodosContext);
if (!ctx) {
throw new Error("use TodosCtx must be used within <TodosProvider>");
}
return ctx;
}
export type { Todo };
Loading…
Cancel
Save