Peace 2 weeks ago
parent 2ba5602b27
commit 8e145cc62e
  1. 71
      lite.todo/app/(tabs)/_layout.tsx
  2. 6
      lite.todo/app/(tabs)/done.tsx
  3. 66
      lite.todo/app/(tabs)/index.tsx
  4. 15
      lite.todo/app/_layout.tsx
  5. 22
      lite.todo/components/todo-list.tsx
  6. 27
      lite.todo/package-lock.json
  7. 1
      lite.todo/package.json
  8. 1
      lite.todo/store/todoProvider.tsx

@ -1,45 +1,42 @@
import { Tabs } from "expo-router";
import { MaterialIcons } from "@expo/vector-icons";
import { TodosProvider } from "@/store/todoProvider";
export default function TabLayout() {
return (
<TodosProvider>
<Tabs
screenOptions={{
headerShown: false,
tabBarStyle: { height: 56 },
tabBarLabelStyle: { fontSize: 12 },
tabBarHideOnKeyboard: true,
<Tabs
screenOptions={{
headerShown: false,
tabBarStyle: { height: 56 },
tabBarLabelStyle: { fontSize: 12 },
tabBarHideOnKeyboard: false,
}}
>
<Tabs.Screen
name="index"
options={{
title: "할 일",
tabBarIcon: ({ color, focused, size }) => (
<MaterialIcons
name={focused ? "check-box" : "check-box-outline-blank"}
size={size ?? 24}
color={color}
/>
),
}}
>
<Tabs.Screen
name="index"
options={{
title: "할 일",
tabBarIcon: ({ color, focused, size }) => (
<MaterialIcons
name={focused ? "check-box" : "check-box-outline-blank"}
size={size ?? 24}
color={color}
/>
),
}}
/>
<Tabs.Screen
name="done"
options={{
title: "완료됨",
tabBarIcon: ({ color, focused, size }) => (
<MaterialIcons
name={focused ? "done-all" : "done"}
size={size ?? 24}
color={color}
/>
),
}}
/>
</Tabs>
</TodosProvider>
/>
<Tabs.Screen
name="done"
options={{
title: "완료됨",
tabBarIcon: ({ color, focused, size }) => (
<MaterialIcons
name={focused ? "done-all" : "done"}
size={size ?? 24}
color={color}
/>
),
}}
/>
</Tabs>
);
}

@ -1,4 +1,4 @@
import TodoList from "@/components/TodoList";
import TodoList from "@/components/todo-list";
import { Button, ButtonText } from "@/components/ui/button";
import { GS } from "@/components/ui/gluestack-ui-provider/gluestack-ui-token";
import { HStack } from "@/components/ui/hstack";
@ -11,8 +11,8 @@ export default function DoneTab() {
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">
<SafeAreaView className="flex-1 px-4 py-1 bg-white dark:bg-neutral-900">
<VStack className="flex-1 gap-3">
<Text className="text-2xl font-bold"> </Text>
<TodoList data={doneTodos} onToggle={toggle} onRemove={remove} />
<HStack className="justify-end">

@ -1,4 +1,4 @@
import TodoList from "@/components/TodoList";
import TodoList from "@/components/todo-list";
import { Button, ButtonText } from "@/components/ui/button";
import { GS } from "@/components/ui/gluestack-ui-provider/gluestack-ui-token";
import { HStack } from "@/components/ui/hstack";
@ -7,11 +7,12 @@ import { Text } from "@/components/ui/text";
import { VStack } from "@/components/ui/vstack";
import { useTodosCtx } from "@/store/todoProvider";
import { useState } from "react";
import { KeyboardAvoidingView, Platform } from "react-native";
import { KeyboardAvoidingView, Platform, View } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
export default function ActiveTab() {
const { activeTodos, leftCount, add, toggle, completeAll } = useTodosCtx();
const { todos, activeTodos, leftCount, add, toggle, completeAll, clearDone } =
useTodosCtx();
const [title, setTitle] = useState("");
const onAdd = () => {
@ -20,33 +21,38 @@ export default function ActiveTab() {
};
return (
<SafeAreaView style={{ flex: 1 }} className="bg-white dark:bg-neutral-900">
<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>
{/* 확인 창 */}
<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>
</KeyboardAvoidingView>
<SafeAreaView className="flex-1 px-4 py-1 bg-white dark:bg-neutral-900">
<VStack className="flex-1 gap-3">
<HStack className="flex justify-between">
<Text className="text-2xl font-bold"> </Text>
</HStack>
{/* 입력 창 */}
<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>
{/* 확인 창 */}
<TodoList data={todos} onToggle={toggle} />
<Button
variant={GS.variant.outline}
action={GS.action.secondary}
onPress={clearDone}
>
<ButtonText> </ButtonText>
</Button>
</VStack>
</SafeAreaView>
);
}

@ -5,15 +5,18 @@ import { Stack } from "expo-router";
import { GluestackUIProvider } from "@/components/ui/gluestack-ui-provider";
import "@/global.css";
import { TodosProvider } from "@/store/todoProvider";
export default function App() {
return (
<GluestackUIProvider mode="light">
<SafeAreaView style={{ flex: 1 }}>
<StatusBar style="auto" />
<Stack screenOptions={{ header: () => null }} />
</SafeAreaView>
</GluestackUIProvider>
<TodosProvider>
<GluestackUIProvider mode="light">
<SafeAreaView style={{ flex: 1 }}>
<StatusBar style="auto" />
<Stack screenOptions={{ header: () => null }} />
</SafeAreaView>
</GluestackUIProvider>
</TodosProvider>
);
}

@ -12,16 +12,24 @@ import {
import { Button, ButtonText } from "./ui/button";
import { GS } from "./ui/gluestack-ui-provider/gluestack-ui-token";
import { CheckIcon, CloseIcon, Icon } from "@/components/ui/icon";
import * as Animatable from "react-native-animatable";
type Props = {
data: Todo[];
onToggle: (id: string) => void;
onRemove?: (id: string) => void;
className?: string;
};
export default function TodoList({ data, onToggle, onRemove }: Props) {
export default function TodoList({
data,
onToggle,
onRemove,
className,
}: Props) {
return (
<FlatList
className={className}
data={data}
keyExtractor={(it) => it.id}
contentContainerStyle={{ paddingBottom: 80 }}
@ -33,9 +41,15 @@ export default function TodoList({ data, onToggle, onRemove }: Props) {
onChange={(_isChecked) => onToggle(item.id)}
value={item.title}
>
<CheckboxIndicator>
<CheckboxIcon as={CheckIcon} />
</CheckboxIndicator>
<Animatable.View
animation={item.done ? "bounce" : undefined}
duration={300}
useNativeDriver
>
<CheckboxIndicator>
<CheckboxIcon as={CheckIcon} />
</CheckboxIndicator>
</Animatable.View>
<CheckboxLabel className="data-[checked=true]:line-through">
{item.title}
</CheckboxLabel>

@ -26,6 +26,7 @@
"react-aria": "^3.33.0",
"react-dom": "^19.1.1",
"react-native": "0.79.6",
"react-native-animatable": "^1.4.0",
"react-native-reanimated": "^4.1.0",
"react-native-safe-area-context": "5.4.0",
"react-native-screens": "~4.11.1",
@ -9340,6 +9341,23 @@
"node": ">= 6"
}
},
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"license": "MIT",
"dependencies": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.13.1"
}
},
"node_modules/prop-types/node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"license": "MIT"
},
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@ -9618,6 +9636,15 @@
}
}
},
"node_modules/react-native-animatable": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/react-native-animatable/-/react-native-animatable-1.4.0.tgz",
"integrity": "sha512-DZwaDVWm2NBvBxf7I0wXKXLKb/TxDnkV53sWhCvei1pRyTX3MVFpkvdYBknNBqPrxYuAIlPxEp7gJOidIauUkw==",
"license": "MIT",
"dependencies": {
"prop-types": "^15.8.1"
}
},
"node_modules/react-native-css-interop": {
"version": "0.1.22",
"resolved": "https://registry.npmjs.org/react-native-css-interop/-/react-native-css-interop-0.1.22.tgz",

@ -27,6 +27,7 @@
"react-aria": "^3.33.0",
"react-dom": "^19.1.1",
"react-native": "0.79.6",
"react-native-animatable": "^1.4.0",
"react-native-reanimated": "^4.1.0",
"react-native-safe-area-context": "5.4.0",
"react-native-screens": "~4.11.1",

@ -6,6 +6,7 @@ type TodosContextValue = ReturnType<typeof useTodos>;
const TodosContext = createContext<TodosContextValue | null>(null);
export function TodosProvider({ children }: { children: React.ReactNode }) {
console.log("[TODO_PROVIDER] MOUNTED!");
const store = useTodos();
return (
<TodosContext.Provider value={store}>{children}</TodosContext.Provider>

Loading…
Cancel
Save