From f9230fa6296dc41b8d8676ea61ea262d1f806fc2 Mon Sep 17 00:00:00 2001 From: Asako Kanno Date: Tue, 2 Dec 2025 21:41:37 +0100 Subject: [PATCH 01/22] first commit --- index.html | 3 ++ package.json | 3 +- src/App.jsx | 24 ++++++++++++- src/components/input/inputCard.jsx | 37 +++++++++++++++++++ src/components/input/inputForm.jsx | 37 +++++++++++++++++++ src/components/input/submitButton.jsx | 23 ++++++++++++ src/components/input/text.jsx | 5 +++ src/components/messages/likeButton.jsx | 45 +++++++++++++++++++++++ src/components/messages/messageCard.jsx | 46 ++++++++++++++++++++++++ src/components/messages/text.jsx | 5 +++ src/components/messages/time.jsx | 5 +++ src/styling/globalStyles.js | 15 ++++++++ src/styling/theme.js | 14 ++++++++ src/styling/typography.js | 7 ++++ src/ts-practice/footer.tsx | 27 ++++++++++++++ src/ts-practice/hero.tsx | 21 +++++++++++ tsconfig.json | 48 +++++++++++++++++++++++++ 17 files changed, 363 insertions(+), 2 deletions(-) create mode 100644 src/components/input/inputCard.jsx create mode 100644 src/components/input/inputForm.jsx create mode 100644 src/components/input/submitButton.jsx create mode 100644 src/components/input/text.jsx create mode 100644 src/components/messages/likeButton.jsx create mode 100644 src/components/messages/messageCard.jsx create mode 100644 src/components/messages/text.jsx create mode 100644 src/components/messages/time.jsx create mode 100644 src/styling/globalStyles.js create mode 100644 src/styling/theme.js create mode 100644 src/styling/typography.js create mode 100644 src/ts-practice/footer.tsx create mode 100644 src/ts-practice/hero.tsx create mode 100644 tsconfig.json diff --git a/index.html b/index.html index d4492e94..2113da52 100644 --- a/index.html +++ b/index.html @@ -3,6 +3,9 @@ + + + Happy Thoughts diff --git a/package.json b/package.json index 2f66d295..107eb724 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ }, "dependencies": { "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "styled-components": "^6.1.19" }, "devDependencies": { "@eslint/js": "^9.21.0", diff --git a/src/App.jsx b/src/App.jsx index 07f2cbdf..605dcde4 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,5 +1,27 @@ +import { GlobalStyles } from "./styling/globalStyles.js" +import { theme } from "./styling/theme.js" +import { Hero } from "./ts-practice/hero" +import { Footer } from "./ts-practice/footer" +import { InputCard } from "./components/input/inputCard.jsx" +import { MessageCard } from "./components/messages/messageCard.jsx" +import { ThemeProvider } from "styled-components" + export const App = () => { return ( -

Happy Thoughts

+ <> + + + +
+ + + + + + +
+
+
+ ) } diff --git a/src/components/input/inputCard.jsx b/src/components/input/inputCard.jsx new file mode 100644 index 00000000..89eb5927 --- /dev/null +++ b/src/components/input/inputCard.jsx @@ -0,0 +1,37 @@ +import styled from "styled-components" +import { Title } from "./text.jsx" +import { InputForm } from "./inputForm.jsx" +import { SubmitButton } from "./submitButton.jsx" + +export const InputCard = () => { + return ( + + + + + <InputForm /> + + <SubmitButton /> + + </StyledInputCard> + </InputCardSection> + ) +} + +const InputCardSection = styled.section` + display: flex; + justify-content: center; + align-items: center; +` + +const StyledInputCard = styled.div` + display: flex; + flex-direction: column; + justify-content: flex-start; + border: 2px solid #000000; + width: 50%; + height: auto; + padding: 20px; + background-color: #f2f0f0; + box-shadow: 7px 7px #1a1a1a; +` \ No newline at end of file diff --git a/src/components/input/inputForm.jsx b/src/components/input/inputForm.jsx new file mode 100644 index 00000000..d8c6eb41 --- /dev/null +++ b/src/components/input/inputForm.jsx @@ -0,0 +1,37 @@ +import { useState } from "react"; +import styled from "styled-components"; + +export const InputForm = () => { + const [textInput, setTextInput] = useState("") + + return ( + <StyledForm> + <input + type="text" + onChange={event => setTextInput(event.target.value)} + value={textInput} + placeholder="React is making me happy!" + /> + </StyledForm> + ) +} + +const StyledForm = styled.form` + display: flex; + flex-direction: column; + justify-content: flex-start; + padding-top: 10px; + width: 100%; + height: 100px; + + input { + display: flex; + justify-content: center; + width: 100%; + height: 100px; + padding: 5px 10px; + border: 2px solid #ccc; + border-radius: 5px; + margin-bottom: 15px; + } +` \ No newline at end of file diff --git a/src/components/input/submitButton.jsx b/src/components/input/submitButton.jsx new file mode 100644 index 00000000..7e3967ea --- /dev/null +++ b/src/components/input/submitButton.jsx @@ -0,0 +1,23 @@ +import styled from "styled-components"; + +export const SubmitButton = () => { + return ( + <StyledButton type="submit"> + ❤️ Send Happy Thought ❤️ + </StyledButton> + ) +} + +const StyledButton = styled.button` + background-color: ${({ theme }) => theme.colors.primary}; + border: none; + border-radius: 50px; + padding: 10px 20px; + font-size: 16px; + cursor: pointer; + transition: background-color 0.3s ease; + + &:hover { + background-color:${({ theme }) => theme.colors.secondary}; + } +` \ No newline at end of file diff --git a/src/components/input/text.jsx b/src/components/input/text.jsx new file mode 100644 index 00000000..89ea7703 --- /dev/null +++ b/src/components/input/text.jsx @@ -0,0 +1,5 @@ +export const Title = () => { + return ( + <p>What's making you happy right now?</p> + ) +} \ No newline at end of file diff --git a/src/components/messages/likeButton.jsx b/src/components/messages/likeButton.jsx new file mode 100644 index 00000000..600ea194 --- /dev/null +++ b/src/components/messages/likeButton.jsx @@ -0,0 +1,45 @@ +import { useState } from 'react' +import styled from 'styled-components' +import { Paragraph } from '../../styling/typography.js' + +export const LikeButton = () => { + const [likeCount, setLikeCount] = useState(0) + + const handleLike = () => { + setLikeCount(likeCount + 1) + } + + return ( + <ButtonWrapper> + <StyledLikeButton type="button" onClick={handleLike}> + ❤️ + </StyledLikeButton> + <Paragraph> x {likeCount}</Paragraph> + </ButtonWrapper> + ) +} + +const ButtonWrapper = styled.div` + display: flex; + flex-direction: row; + align-items: center; + gap: 10px; +` + +const StyledLikeButton = styled.button` + background-color: ${({ theme }) => theme.colors.primary}; + border: none; + border-radius: 50%; + width: 40px; + height: 40px; + font-size: 20px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: background-color 0.3s ease; + + &:hover { + background-color: ${({ theme }) => theme.colors.secondary}; + } +` \ No newline at end of file diff --git a/src/components/messages/messageCard.jsx b/src/components/messages/messageCard.jsx new file mode 100644 index 00000000..4c47e686 --- /dev/null +++ b/src/components/messages/messageCard.jsx @@ -0,0 +1,46 @@ +import styled from "styled-components"; +import { LikeButton } from "./likeButton.jsx"; +import { HappyText } from "./text.jsx"; +import { Time } from "./time.jsx"; + +export const MessageCard = () => { + return ( + <MessageSection> + <StyledLikeCard> + <HappyText /> + <LikeButtonWrapper> + <LikeButton /> + + <Time /> + </LikeButtonWrapper> + </StyledLikeCard> + </MessageSection> + ) +} + +const MessageSection = styled.section` + display: flex; + justify-content: center; + align-items: center; + margin: 30px 0; +` + +const StyledLikeCard = styled.div` + display: flex; + flex-direction: column; + justify-content: flex-start; + gap: 20px; + border: 2px solid ${({ theme }) => theme.colors.border }; + width: 50%; + height: auto; + padding: 20px; + background-color: ${({ theme }) => theme.colors.cardBackground }; + box-shadow: 7px 7px #1a1a1a; +` + +const LikeButtonWrapper = styled.div` + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; +` \ No newline at end of file diff --git a/src/components/messages/text.jsx b/src/components/messages/text.jsx new file mode 100644 index 00000000..4de94b3a --- /dev/null +++ b/src/components/messages/text.jsx @@ -0,0 +1,5 @@ +export const HappyText = () => { + return ( + <p>this is my happy thought</p> + ) +} \ No newline at end of file diff --git a/src/components/messages/time.jsx b/src/components/messages/time.jsx new file mode 100644 index 00000000..73e536d7 --- /dev/null +++ b/src/components/messages/time.jsx @@ -0,0 +1,5 @@ +export const Time = () => { + return ( + <p>2 minutes ago</p> + ) +} \ No newline at end of file diff --git a/src/styling/globalStyles.js b/src/styling/globalStyles.js new file mode 100644 index 00000000..273c7c8f --- /dev/null +++ b/src/styling/globalStyles.js @@ -0,0 +1,15 @@ +import { createGlobalStyle } from "styled-components"; + +export const GlobalStyles = createGlobalStyle` + * { + margin: 0; + padding: 0; + box-sizing: border-box; + } + + body { + background-color: #fafafa; + color: #1a1a1a; + font-family: Google Sans Code, sans-serif; + } +` \ No newline at end of file diff --git a/src/styling/theme.js b/src/styling/theme.js new file mode 100644 index 00000000..a89bcf06 --- /dev/null +++ b/src/styling/theme.js @@ -0,0 +1,14 @@ +export const theme = { + colors: { + primary: "#ffadad", + secondary: "#ff6f6f", + cardBackground: "#ffffff", + text: "#1a1a1a", + border: "#000000" + }, + breakpoints: { + mobile: "480px", + tablet: "768px", + desktop: "1024px" + } +} \ No newline at end of file diff --git a/src/styling/typography.js b/src/styling/typography.js new file mode 100644 index 00000000..0c37e334 --- /dev/null +++ b/src/styling/typography.js @@ -0,0 +1,7 @@ +import styled from "styled-components"; + +export const Heading1 = styled.h1` + font-size: 40px; +` +export const Paragraph = styled.p` + font-size: 1rem;` \ No newline at end of file diff --git a/src/ts-practice/footer.tsx b/src/ts-practice/footer.tsx new file mode 100644 index 00000000..fd8e3a4d --- /dev/null +++ b/src/ts-practice/footer.tsx @@ -0,0 +1,27 @@ +import { styled } from "styled-components"; +import { Paragraph } from "../styling/typography.js"; + +type Props = { + text: string; +} + +export const Footer = ({text}:Props) => { + return ( + <FooterSection> + <FooterText>{text}</FooterText> + </FooterSection> + ) +} + +const FooterSection = styled.footer` + padding: 20px 0; + background-color: #ffadad; + position: absolute; + bottom: 0; + width: 100%; +` +const FooterText = styled(Paragraph)` + display: flex; + align-items: center; + justify-content: center; +` \ No newline at end of file diff --git a/src/ts-practice/hero.tsx b/src/ts-practice/hero.tsx new file mode 100644 index 00000000..2b678cb2 --- /dev/null +++ b/src/ts-practice/hero.tsx @@ -0,0 +1,21 @@ +import { styled } from "styled-components"; +import { Heading1 } from "../styling/typography.js"; + +type Props = { + text: string; +} + +export const Hero = ({ text }: Props) => { + return ( + <HeroSection> + <Heading1>{text}</Heading1> + </HeroSection> + ) +} + +const HeroSection = styled.div` + display: flex; + align-items: center; + justify-content: center; + height: 100px; +` \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..5066fe90 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,48 @@ +{ + // Visit https://aka.ms/tsconfig to read more about this file + "compilerOptions": { + // File Layout + // "rootDir": "./src", + // "outDir": "./dist", + + // Environment Settings + // See also https://aka.ms/tsconfig/module + "module": "nodenext", + "target": "esnext", + "types": [], + // For nodejs: + // "lib": ["esnext"], + // "types": ["node"], + // and npm install -D @types/node + + // Other Outputs + "sourceMap": true, + "declaration": true, + "declarationMap": true, + + // Stricter Typechecking Options + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + + // Style Options + // "noImplicitReturns": true, + // "noImplicitOverride": true, + // "noUnusedLocals": true, + // "noUnusedParameters": true, + // "noFallthroughCasesInSwitch": true, + // "noPropertyAccessFromIndexSignature": true, + + // Recommended Options + "strict": true, + "jsx": "react-jsx", + "verbatimModuleSyntax": true, + "isolatedModules": true, + "noUncheckedSideEffectImports": true, + "moduleDetection": "force", + "skipLibCheck": true, + + "allowJs": true, + "checkJs": false, + "noEmit": true + } +} From 7765281e3ee9d1fe406658e543639f78f19a9e4e Mon Sep 17 00:00:00 2001 From: Asako Kanno <asa5ng13@gmail.com> Date: Wed, 3 Dec 2025 18:33:33 +0100 Subject: [PATCH 02/22] add useState to inputFrom --- src/App.jsx | 22 ++++++++-- src/{ts-practice => components}/footer.tsx | 0 src/components/header.tsx | 21 +++++++++ src/components/input/inputCard.jsx | 17 +++++--- src/components/input/inputForm.jsx | 30 +++++++++---- .../input/{text.jsx => inputTitle.jsx} | 0 src/components/input/submitButton.jsx | 5 +++ src/components/messages/messageCard.jsx | 12 +++++- src/components/messages/messageList.jsx | 0 .../messages/{text.jsx => messageText.jsx} | 0 src/styling/animatedTitle.jsx | 43 +++++++++++++++++++ src/styling/theme.js | 7 +-- src/theme.d.ts | 18 ++++++++ src/ts-practice/hero.tsx | 21 --------- 14 files changed, 151 insertions(+), 45 deletions(-) rename src/{ts-practice => components}/footer.tsx (100%) create mode 100644 src/components/header.tsx rename src/components/input/{text.jsx => inputTitle.jsx} (100%) create mode 100644 src/components/messages/messageList.jsx rename src/components/messages/{text.jsx => messageText.jsx} (100%) create mode 100644 src/styling/animatedTitle.jsx create mode 100644 src/theme.d.ts delete mode 100644 src/ts-practice/hero.tsx diff --git a/src/App.jsx b/src/App.jsx index 605dcde4..0fb85580 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,21 +1,35 @@ +import { useState } from "react" + import { GlobalStyles } from "./styling/globalStyles.js" import { theme } from "./styling/theme.js" -import { Hero } from "./ts-practice/hero" -import { Footer } from "./ts-practice/footer" +import { Header } from "./components/header.js" +import { Footer } from "./components/footer.js" import { InputCard } from "./components/input/inputCard.jsx" import { MessageCard } from "./components/messages/messageCard.jsx" import { ThemeProvider } from "styled-components" export const App = () => { + const [messages, setMessages] = useState([]) + + const addMessage = (newText) => { + const newMessage = { + text: newText, + hearts: 0, + createdAt: Date.now(), + } + + setMessages([...messages, newMessage]) + } + return ( <> <ThemeProvider theme={theme}> <GlobalStyles /> <main> - <Hero text="Happy Thoughts"/> + <Header text="Happy Thoughts"/> - <InputCard /> + <InputCard onSubmit={addMessage} /> <MessageCard /> diff --git a/src/ts-practice/footer.tsx b/src/components/footer.tsx similarity index 100% rename from src/ts-practice/footer.tsx rename to src/components/footer.tsx diff --git a/src/components/header.tsx b/src/components/header.tsx new file mode 100644 index 00000000..a21b416e --- /dev/null +++ b/src/components/header.tsx @@ -0,0 +1,21 @@ +import { styled } from "styled-components"; +import { AnimatedTitle } from "../styling/animatedTitle.jsx"; + +type Props = { + text: string; +} + +export const Header = ({ text }: Props) => { + return ( + <HeaderSection> + <AnimatedTitle text={text} /> + </HeaderSection> + ) +} + +const HeaderSection = styled.div` + display: flex; + align-items: center; + justify-content: center; + height: 100px; +` \ No newline at end of file diff --git a/src/components/input/inputCard.jsx b/src/components/input/inputCard.jsx index 89eb5927..c7ce43ed 100644 --- a/src/components/input/inputCard.jsx +++ b/src/components/input/inputCard.jsx @@ -1,17 +1,14 @@ import styled from "styled-components" -import { Title } from "./text.jsx" +import { Title } from "./inputTitle.jsx" import { InputForm } from "./inputForm.jsx" -import { SubmitButton } from "./submitButton.jsx" -export const InputCard = () => { +export const InputCard = ({onSubmit}) => { return ( <InputCardSection> <StyledInputCard> <Title /> - <InputForm /> - - <SubmitButton /> + <InputForm onSubmit={onSubmit} /> </StyledInputCard> </InputCardSection> @@ -34,4 +31,12 @@ const StyledInputCard = styled.div` padding: 20px; background-color: #f2f0f0; box-shadow: 7px 7px #1a1a1a; + + @media ${({ theme }) => theme.breakpoints.mobile} { + width: 90%; + } + + @media ${({ theme }) => theme.breakpoints.tablet} { + width: 70%; + } ` \ No newline at end of file diff --git a/src/components/input/inputForm.jsx b/src/components/input/inputForm.jsx index d8c6eb41..e6587e98 100644 --- a/src/components/input/inputForm.jsx +++ b/src/components/input/inputForm.jsx @@ -1,17 +1,27 @@ -import { useState } from "react"; -import styled from "styled-components"; +import { useState } from "react" +import styled from "styled-components" +import { SubmitButton } from "./submitButton.jsx" -export const InputForm = () => { + +export const InputForm = ({ onSubmit }) => { const [textInput, setTextInput] = useState("") + const handleSubmit = (event) => { + event.preventDefault() + if (textInput.trim() === "") return + + onSubmit(textInput) + setTextInput("") // Clear the input after submission + } + return ( - <StyledForm> - <input - type="text" + <StyledForm onSubmit={handleSubmit}> + <textarea onChange={event => setTextInput(event.target.value)} value={textInput} placeholder="React is making me happy!" /> + <SubmitButton type="submit" /> </StyledForm> ) } @@ -22,16 +32,18 @@ const StyledForm = styled.form` justify-content: flex-start; padding-top: 10px; width: 100%; - height: 100px; + height: 150px; - input { + textarea { display: flex; justify-content: center; width: 100%; - height: 100px; + height: 100%; padding: 5px 10px; border: 2px solid #ccc; border-radius: 5px; margin-bottom: 15px; + white-space: pre-wrap; + word-wrap: break-word; } ` \ No newline at end of file diff --git a/src/components/input/text.jsx b/src/components/input/inputTitle.jsx similarity index 100% rename from src/components/input/text.jsx rename to src/components/input/inputTitle.jsx diff --git a/src/components/input/submitButton.jsx b/src/components/input/submitButton.jsx index 7e3967ea..ffbbfeff 100644 --- a/src/components/input/submitButton.jsx +++ b/src/components/input/submitButton.jsx @@ -12,6 +12,7 @@ const StyledButton = styled.button` background-color: ${({ theme }) => theme.colors.primary}; border: none; border-radius: 50px; + width: 50%; padding: 10px 20px; font-size: 16px; cursor: pointer; @@ -20,4 +21,8 @@ const StyledButton = styled.button` &:hover { background-color:${({ theme }) => theme.colors.secondary}; } + + @media ${({ theme }) => theme.breakpoints.mobile} { + width: 100%; + } ` \ No newline at end of file diff --git a/src/components/messages/messageCard.jsx b/src/components/messages/messageCard.jsx index 4c47e686..51104c68 100644 --- a/src/components/messages/messageCard.jsx +++ b/src/components/messages/messageCard.jsx @@ -1,6 +1,6 @@ import styled from "styled-components"; import { LikeButton } from "./likeButton.jsx"; -import { HappyText } from "./text.jsx"; +import { HappyText } from "./messageText.jsx"; import { Time } from "./time.jsx"; export const MessageCard = () => { @@ -36,7 +36,15 @@ const StyledLikeCard = styled.div` padding: 20px; background-color: ${({ theme }) => theme.colors.cardBackground }; box-shadow: 7px 7px #1a1a1a; -` + + @media ${({ theme }) => theme.breakpoints.mobile} { + width: 90%; + } + + @media ${({ theme }) => theme.breakpoints.tablet} { + width: 70%; + } + ` const LikeButtonWrapper = styled.div` display: flex; diff --git a/src/components/messages/messageList.jsx b/src/components/messages/messageList.jsx new file mode 100644 index 00000000..e69de29b diff --git a/src/components/messages/text.jsx b/src/components/messages/messageText.jsx similarity index 100% rename from src/components/messages/text.jsx rename to src/components/messages/messageText.jsx diff --git a/src/styling/animatedTitle.jsx b/src/styling/animatedTitle.jsx new file mode 100644 index 00000000..2ae5d388 --- /dev/null +++ b/src/styling/animatedTitle.jsx @@ -0,0 +1,43 @@ +import styled, { keyframes } from "styled-components" + +export const AnimatedTitle = ({ text }) => { + return ( + <Title> + {text.split("").map((char, index) => ( + <Letter + key={index} + delay={index} + > + {char === " " ? "\u00A0" : char} + </Letter> + ))} + + ) +} + +const bounce = keyframes` +0% { transform: translateY(0); } +20% { transform: translateY(-10px); } +40% { transform: translateY(0); } +80% { transform: translateY(0); } +100% { transform: translateY(0); } +` + +const Title = styled.h1` + font-size: 48px; + font-weight: 700; + display: flex; + gap: 2px; + justify-content: center; + margin: 0; + + @media ${({ theme }) => theme.breakpoints.mobile} { + font-size: 32px; + } +` + +const Letter = styled.span` + display: inline-block; + animation: ${bounce} 1.3s ease-in-out infinite; + animation-delay: ${({ delay }) => delay * 0.05}s; +` \ No newline at end of file diff --git a/src/styling/theme.js b/src/styling/theme.js index a89bcf06..57b9d3a6 100644 --- a/src/styling/theme.js +++ b/src/styling/theme.js @@ -6,9 +6,10 @@ export const theme = { text: "#1a1a1a", border: "#000000" }, + breakpoints: { - mobile: "480px", - tablet: "768px", - desktop: "1024px" + mobile: "(max-width: 480px)", + tablet: "(min-width: 481px) and (max-width: 1023px)", + desktop: "(min-width: 1024px)" } } \ No newline at end of file diff --git a/src/theme.d.ts b/src/theme.d.ts new file mode 100644 index 00000000..e66524e7 --- /dev/null +++ b/src/theme.d.ts @@ -0,0 +1,18 @@ +// styled.d.ts +import "styled-components"; + +declare module "styled-components" { + export interface DefaultTheme { + colors: { + primary: string; + secondary: string; + cardBackground: string; + text: string; + }; + breakpoints: { + mobile: string; + tablet: string; + desktop: string; + }; + } +} diff --git a/src/ts-practice/hero.tsx b/src/ts-practice/hero.tsx deleted file mode 100644 index 2b678cb2..00000000 --- a/src/ts-practice/hero.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { styled } from "styled-components"; -import { Heading1 } from "../styling/typography.js"; - -type Props = { - text: string; -} - -export const Hero = ({ text }: Props) => { - return ( - - {text} - - ) -} - -const HeroSection = styled.div` - display: flex; - align-items: center; - justify-content: center; - height: 100px; -` \ No newline at end of file From 7ba0b00059869014b1d2087885ee24d5b596a314 Mon Sep 17 00:00:00 2001 From: Asako Kanno Date: Thu, 4 Dec 2025 17:17:25 +0100 Subject: [PATCH 03/22] add message function --- src/App.jsx | 21 +++++++++++++++------ src/components/input/inputCard.jsx | 6 +++--- src/components/messages/happyText.jsx | 5 +++++ src/components/messages/likeButton.jsx | 20 +++++++++----------- src/components/messages/messageCard.jsx | 14 +++++++++----- src/components/messages/messageList.jsx | 20 ++++++++++++++++++++ src/components/messages/messageText.jsx | 5 ----- src/components/messages/time.jsx | 22 ++++++++++++++++++++-- src/styling/theme.js | 2 ++ src/theme.d.ts | 3 +++ 10 files changed, 86 insertions(+), 32 deletions(-) create mode 100644 src/components/messages/happyText.jsx delete mode 100644 src/components/messages/messageText.jsx diff --git a/src/App.jsx b/src/App.jsx index 0fb85580..7260dd7b 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,12 +1,12 @@ import { useState } from "react" +import { ThemeProvider } from "styled-components" import { GlobalStyles } from "./styling/globalStyles.js" import { theme } from "./styling/theme.js" import { Header } from "./components/header.js" import { Footer } from "./components/footer.js" import { InputCard } from "./components/input/inputCard.jsx" -import { MessageCard } from "./components/messages/messageCard.jsx" -import { ThemeProvider } from "styled-components" +import { MessageList } from "./components/messages/messageList.jsx" export const App = () => { const [messages, setMessages] = useState([]) @@ -18,7 +18,13 @@ export const App = () => { createdAt: Date.now(), } - setMessages([...messages, newMessage]) + setMessages([newMessage, ...messages]) // Add new message at the beginning of the array + } + + const increaseHeart = (index) => { + const updated = [...messages] + updated[index].hearts += 1 + setMessages(updated) } return ( @@ -26,15 +32,18 @@ export const App = () => { -
+
- + -
+
) diff --git a/src/components/input/inputCard.jsx b/src/components/input/inputCard.jsx index c7ce43ed..9c59c9a0 100644 --- a/src/components/input/inputCard.jsx +++ b/src/components/input/inputCard.jsx @@ -25,12 +25,12 @@ const StyledInputCard = styled.div` display: flex; flex-direction: column; justify-content: flex-start; - border: 2px solid #000000; + border: 2px solid ${({ theme }) => theme.colors.border }; width: 50%; height: auto; padding: 20px; - background-color: #f2f0f0; - box-shadow: 7px 7px #1a1a1a; + background-color: ${({ theme }) => theme.colors.formBackground }; + box-shadow: 7px 7px ${({ theme }) => theme.colors.border }; @media ${({ theme }) => theme.breakpoints.mobile} { width: 90%; diff --git a/src/components/messages/happyText.jsx b/src/components/messages/happyText.jsx new file mode 100644 index 00000000..7a43b8f8 --- /dev/null +++ b/src/components/messages/happyText.jsx @@ -0,0 +1,5 @@ +export const HappyText = ({ text }) => { + return ( +

{text}

+ ) +} \ No newline at end of file diff --git a/src/components/messages/likeButton.jsx b/src/components/messages/likeButton.jsx index 600ea194..5e80c818 100644 --- a/src/components/messages/likeButton.jsx +++ b/src/components/messages/likeButton.jsx @@ -1,20 +1,17 @@ -import { useState } from 'react' import styled from 'styled-components' import { Paragraph } from '../../styling/typography.js' -export const LikeButton = () => { - const [likeCount, setLikeCount] = useState(0) - - const handleLike = () => { - setLikeCount(likeCount + 1) - } - +export const LikeButton = ({ hearts, liked, onLike }) => { return ( - + ❤️ - x {likeCount} + x {hearts} ) } @@ -27,7 +24,8 @@ const ButtonWrapper = styled.div` ` const StyledLikeButton = styled.button` - background-color: ${({ theme }) => theme.colors.primary}; + background-color: ${({ theme, liked }) => + liked ? theme.colors.primary : theme.colors.formBackground }; border: none; border-radius: 50%; width: 40px; diff --git a/src/components/messages/messageCard.jsx b/src/components/messages/messageCard.jsx index 51104c68..8bc50bb2 100644 --- a/src/components/messages/messageCard.jsx +++ b/src/components/messages/messageCard.jsx @@ -1,17 +1,21 @@ import styled from "styled-components"; import { LikeButton } from "./likeButton.jsx"; -import { HappyText } from "./messageText.jsx"; +import { HappyText } from "./happyText.jsx"; import { Time } from "./time.jsx"; -export const MessageCard = () => { +export const MessageCard = ({ id, text, hearts, onLike, createdAt }) => { return ( - + - + onLike(id)} + liked={hearts > 0} + /> - diff --git a/src/components/messages/messageList.jsx b/src/components/messages/messageList.jsx index e69de29b..82644723 100644 --- a/src/components/messages/messageList.jsx +++ b/src/components/messages/messageList.jsx @@ -0,0 +1,20 @@ +import { MessageCard } from "./messageCard.jsx"; + +export const MessageList = ({ messages, onLike }) => { + return ( +
+ {messages.map((message, index) => { + return ( + + ) + })} +
+ ) +} \ No newline at end of file diff --git a/src/components/messages/messageText.jsx b/src/components/messages/messageText.jsx deleted file mode 100644 index 4de94b3a..00000000 --- a/src/components/messages/messageText.jsx +++ /dev/null @@ -1,5 +0,0 @@ -export const HappyText = () => { - return ( -

this is my happy thought

- ) -} \ No newline at end of file diff --git a/src/components/messages/time.jsx b/src/components/messages/time.jsx index 73e536d7..ad763950 100644 --- a/src/components/messages/time.jsx +++ b/src/components/messages/time.jsx @@ -1,5 +1,23 @@ -export const Time = () => { +export const Time = ({ createdAt }) => { + const formatTimeAgo = (timestamp) => { + const now = Date.now() + const seconds = Math.floor((now - timestamp) / 1000) + const minutes = Math.floor(seconds / 60) + const hours = Math.floor(minutes / 60) + const days = Math.floor(hours / 24) + + if (seconds < 60) { + return `${seconds} seconds ago` + } else if (minutes < 60) { + return `${minutes} minutes ago` + } else if (hours < 24) { + return `${hours} hours ago` + } else { + return `${days} days ago` + } + } + return ( -

2 minutes ago

+ {formatTimeAgo(createdAt)} ) } \ No newline at end of file diff --git a/src/styling/theme.js b/src/styling/theme.js index 57b9d3a6..839b0e77 100644 --- a/src/styling/theme.js +++ b/src/styling/theme.js @@ -2,7 +2,9 @@ export const theme = { colors: { primary: "#ffadad", secondary: "#ff6f6f", + formBackground: "#f2f0f0", cardBackground: "#ffffff", + likeBtnBackground: "#ffe3e3", text: "#1a1a1a", border: "#000000" }, diff --git a/src/theme.d.ts b/src/theme.d.ts index e66524e7..240d7cb0 100644 --- a/src/theme.d.ts +++ b/src/theme.d.ts @@ -6,8 +6,11 @@ declare module "styled-components" { colors: { primary: string; secondary: string; + formBackground: string, + likeBtnBackground: string, cardBackground: string; text: string; + border: string; }; breakpoints: { mobile: string; From 4896ae69279458777ee788bba3a8379008f13872 Mon Sep 17 00:00:00 2001 From: Asako Kanno Date: Thu, 4 Dec 2025 18:37:33 +0100 Subject: [PATCH 04/22] add favicon --- index.html | 2 +- public/favicon.ico | Bin 0 -> 1150 bytes src/App.jsx | 2 +- src/components/input/inputForm.jsx | 1 + src/components/input/submitButton.jsx | 7 +++++-- src/components/messages/happyText.jsx | 13 +++++++++++-- src/components/messages/messageCard.jsx | 4 ++-- src/components/messages/messageList.jsx | 1 + 8 files changed, 22 insertions(+), 8 deletions(-) create mode 100644 public/favicon.ico diff --git a/index.html b/index.html index 2113da52..a8465b8b 100644 --- a/index.html +++ b/index.html @@ -2,7 +2,7 @@ - + diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..4ba0956000c5ddedc217e761cc5276e76fc75073 GIT binary patch literal 1150 zcmchV&n`nz5XQf1ZKW$ot;#~Z0(z-LNJJ{}0xFU67*-yDClD{-4Qfm5bz>)DOCq5( z$8YZRq<@0+TDZ;EGv}N6=FFKhQjTA-DE`YjDoE3k7625C^DGj+fBULIN_sFm%V#>% zRKpZG4ARONIaY~T0MG5cCW+wavZ+}#-3 zn4?XsX}s4-*unnDpzjb{AJA`Mzh}_D5o?-wehK@#p1;ByV%>4QhBJ-QKKQKG*c)%T z?cMLeA!zn& { createdAt: Date.now(), } - setMessages([newMessage, ...messages]) // Add new message at the beginning of the array + setMessages([newMessage, ...messages]) // Add new message on top of the list } const increaseHeart = (index) => { diff --git a/src/components/input/inputForm.jsx b/src/components/input/inputForm.jsx index e6587e98..b5f48573 100644 --- a/src/components/input/inputForm.jsx +++ b/src/components/input/inputForm.jsx @@ -20,6 +20,7 @@ export const InputForm = ({ onSubmit }) => { onChange={event => setTextInput(event.target.value)} value={textInput} placeholder="React is making me happy!" + maxLength={140} /> diff --git a/src/components/input/submitButton.jsx b/src/components/input/submitButton.jsx index ffbbfeff..b3296c9c 100644 --- a/src/components/input/submitButton.jsx +++ b/src/components/input/submitButton.jsx @@ -1,8 +1,11 @@ import styled from "styled-components"; -export const SubmitButton = () => { +export const SubmitButton = ( textInput ) => { return ( - + 140} + > ❤️ Send Happy Thought ❤️ ) diff --git a/src/components/messages/happyText.jsx b/src/components/messages/happyText.jsx index 7a43b8f8..f6cd236c 100644 --- a/src/components/messages/happyText.jsx +++ b/src/components/messages/happyText.jsx @@ -1,5 +1,14 @@ +import styled from "styled-components" + export const HappyText = ({ text }) => { return ( -

{text}

+ {text} ) -} \ No newline at end of file +} + +const StyledText = styled.p` + white-space: normal; + overflow-wrap: break-word; + word-break: break-word; + line-height: 1.5; +` \ No newline at end of file diff --git a/src/components/messages/messageCard.jsx b/src/components/messages/messageCard.jsx index 8bc50bb2..6fc3dcdb 100644 --- a/src/components/messages/messageCard.jsx +++ b/src/components/messages/messageCard.jsx @@ -3,7 +3,7 @@ import { LikeButton } from "./likeButton.jsx"; import { HappyText } from "./happyText.jsx"; import { Time } from "./time.jsx"; -export const MessageCard = ({ id, text, hearts, onLike, createdAt }) => { +export const MessageCard = ({ id, text, hearts, onLike, liked, createdAt }) => { return ( @@ -12,7 +12,7 @@ export const MessageCard = ({ id, text, hearts, onLike, createdAt }) => { onLike(id)} - liked={hearts > 0} + liked={liked} />
-