From c5258f80684d1e28bd1bc20c345335a29c304302 Mon Sep 17 00:00:00 2001 From: Maria Rushkova <66270386+mrushkova@users.noreply.github.com> Date: Tue, 28 Sep 2021 13:36:56 +0200 Subject: [PATCH] Add channels and improve styles (#15) --- .../react-chat/src/components/Channels.tsx | 225 +++++++++++++++++- packages/react-chat/src/components/Chat.tsx | 26 +- .../src/components/Chat/ChatBody.tsx | 36 ++- .../src/components/Chat/ChatInput.tsx | 53 +++-- .../src/components/Chat/ChatMessages.tsx | 95 ++++---- .../react-chat/src/components/Members.tsx | 14 +- .../react-chat/src/components/ReactChat.tsx | 2 +- .../react-chat/src/helpers/channelsMock.ts | 32 +++ packages/react-chat/src/styles/themes.ts | 12 + 9 files changed, 405 insertions(+), 90 deletions(-) create mode 100644 packages/react-chat/src/helpers/channelsMock.ts diff --git a/packages/react-chat/src/components/Channels.tsx b/packages/react-chat/src/components/Channels.tsx index 817f4cc..77813c6 100644 --- a/packages/react-chat/src/components/Channels.tsx +++ b/packages/react-chat/src/components/Channels.tsx @@ -1,18 +1,235 @@ import React from "react"; import styled from "styled-components"; +import { ChannelData, channels } from "../helpers/channelsMock"; import { Theme } from "../styles/themes"; interface ChannelsProps { theme: Theme; + icon: string; + name: string; + members: number; + setActiveChannel: (val: ChannelData) => void; + activeChannelId: number; } -export function Channels({ theme }: ChannelsProps) { - return Channels; +export function Channels({ + theme, + icon, + name, + members, + setActiveChannel, + activeChannelId, +}: ChannelsProps) { + return ( + + + + + {name} + {members} members + + + + {channels.map((channel) => ( + { + setActiveChannel(channel); + }} + /> + ))} + + + ); } -const ChannelsWrapper = styled.div` - width: 33%; +interface ChannelProps { + theme: Theme; + channel: ChannelData; + isActive: boolean; + activeView?: boolean; + onClick?: () => void; +} + +export function Channel({ + theme, + channel, + isActive, + activeView, + onClick, +}: ChannelProps) { + return ( + + + + {!channel.icon && channel.name.slice(0, 1).toUpperCase()} + + + + # {channel.name} + + {activeView && ( + + {" "} + {channel.members} members + + )} + + + {channel.notifications && !activeView && ( + + {channel.notifications} + + )} + + ); +} +interface ThemeProps { + theme: Theme; +} + +const ChannelsWrapper = styled.div` + width: 21%; height: 100%; background-color: ${({ theme }) => theme.sectionBackgroundColor}; + padding: 10px 0.6%; + display: flex; + flex-direction: column; +`; + +const Community = styled.div` + display: flex; + margin-bottom: 16px; +`; + +const CommunityLogo = styled.img` + width: 36px; + height: 36px; + border-radius: 50%; + margin-left: 10px; +`; + +const CommunityInfo = styled.div` + display: flex; + flex-direction: column; + margin-left: 8px; +`; + +const CommunityName = styled.h1` + font-weight: 500; + font-size: 15px; + line-height: 22px; + color: ${({ theme }) => theme.textPrimaryColor}; +`; + +const MembersAmount = styled.p` + font-size: 12px; + line-height: 16px; + letter-spacing: 0.1px; + color: ${({ theme }) => theme.textSecondaryColor}; +`; + +const ChannelList = styled.div` + display: flex; + flex-direction: column; +`; + +const ChannelWrapper = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px; + cursor: pointer; + + &.active { + background-color: ${({ theme }) => theme.activeChannelBackground}; + border-radius: 8px; + } +`; + +const ChannelInfo = styled.div` + display: flex; + align-items: center; +`; + +const ChannelTextInfo = styled.div` + display: flex; + flex-direction: column; +`; + +const ChannelLogo = styled.div` + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; + margin-right: 10px; + border-radius: 50%; + font-weight: bold; + font-size: 15px; + line-height: 20px; + background-color: ${({ theme }) => theme.iconColor}; + background-size: cover; + background-repeat: no-repeat; + color: ${({ theme }) => theme.iconTextColor}; + + &.active { + width: 36px; + height: 36px; + font-size: 20px; + line-height: 20px; + } +`; + +const ChannelName = styled.p` + color: ${({ theme }) => theme.textPrimaryColor}; + font-weight: 500; + font-size: 15px; + line-height: 22px; + opacity: 0.7; + + &.active, + &.notified { + opacity: 1; + } + + &.muted { + opacity: 0.4; + } + + &.notified { + font-weight: 600; + } +`; + +const NotificationBagde = styled.div` + width: 24px; + height: 24px; + border-radius: 50%; + font-size: 12px; + line-height: 16px; + background-color: ${({ theme }) => theme.notificationColor}; + color: ${({ theme }) => theme.bodyBackgroundColor}; + display: flex; + align-items: center; + justify-content: center; `; diff --git a/packages/react-chat/src/components/Chat.tsx b/packages/react-chat/src/components/Chat.tsx index a58d01c..3249760 100644 --- a/packages/react-chat/src/components/Chat.tsx +++ b/packages/react-chat/src/components/Chat.tsx @@ -1,6 +1,7 @@ -import React from "react"; +import React, { useState } from "react"; import styled from "styled-components"; +import { ChannelData, channels } from "../helpers/channelsMock"; import { Theme } from "../styles/themes"; import { Channels } from "./Channels"; @@ -9,16 +10,27 @@ import { Members } from "./Members"; interface ChatProps { theme: Theme; - channels?: boolean; - members?: boolean; + channelsON?: boolean; + membersON?: boolean; } -export function Chat({ theme, channels, members }: ChatProps) { +export function Chat({ theme, channelsON, membersON }: ChatProps) { + const [activeChannel, setActiveChannel] = useState(channels[0]); + return ( - {channels && } - - {members && } + {channelsON && ( + + )} + + {membersON && } ); } diff --git a/packages/react-chat/src/components/Chat/ChatBody.tsx b/packages/react-chat/src/components/Chat/ChatBody.tsx index 5942c25..7efb985 100644 --- a/packages/react-chat/src/components/Chat/ChatBody.tsx +++ b/packages/react-chat/src/components/Chat/ChatBody.tsx @@ -1,12 +1,20 @@ import React, { useCallback, useState } from "react"; import styled from "styled-components"; +import { ChannelData } from "../../helpers/channelsMock"; +import { Theme } from "../../styles/themes"; +import { Channel } from "../Channels"; import { ChatMessage } from "../models/ChatMessage"; import { ChatInput } from "./ChatInput"; import { ChatMessages } from "./ChatMessages"; -export function ChatBody() { +interface ChatBodyProps { + theme: Theme; + channel: ChannelData; +} + +export function ChatBody({ theme, channel }: ChatBodyProps) { const [messages, setMessages] = useState([]); const addMessage = useCallback( @@ -20,16 +28,32 @@ export function ChatBody() { ); return ( - - - + + + + + + ); } +interface ThemeProps { + theme: Theme; +} -const ChatBodyWrapper = styled.div` +const ChatBodyWrapper = styled.div` + display: flex; + flex-direction: column; flex: 1; height: 100%; - max-width: 50%; background: ${({ theme }) => theme.bodyBackgroundColor}; `; + +const ChannelWrapper = styled.div` + padding-left: 8px; +`; diff --git a/packages/react-chat/src/components/Chat/ChatInput.tsx b/packages/react-chat/src/components/Chat/ChatInput.tsx index cbce356..2355e0a 100644 --- a/packages/react-chat/src/components/Chat/ChatInput.tsx +++ b/packages/react-chat/src/components/Chat/ChatInput.tsx @@ -1,41 +1,56 @@ import React, { useState } from "react"; import styled from "styled-components"; +import { Theme } from "../../styles/themes"; + type ChatInputProps = { + theme: Theme; addMessage: (message: string) => void; }; -export function ChatInput({ addMessage }: ChatInputProps) { +export function ChatInput({ theme, addMessage }: ChatInputProps) { const [content, setContent] = useState(""); return ( - setContent(e.target.value)} - onKeyPress={(e) => { - if (e.key == "Enter") { - addMessage(content); - setContent(""); - } - }} - /> + + setContent(e.target.value)} + onKeyPress={(e) => { + if (e.key == "Enter") { + addMessage(content); + setContent(""); + } + }} + /> + ); } -const Input = styled.input` +interface ThemeProps { + theme: Theme; +} + +const InputWrapper = styled.div` + display: flex; + padding: 6px 8px 6px 10px; +`; +const Input = styled.input` width: 100%; - margin-left: 8px; - margin-right: 8px; - margin-bottom: 4px; + margin-left: 10px; height: 40px; - background: #eef2f5; + background: ${({ theme }) => theme.inputColor}; border-radius: 36px 16px 4px 36px; - border: 0px; + border: 1px solid ${({ theme }) => theme.inputColor}; padding-left: 12px; outline: none; + font-size: 15px; + line-height: 22px; &:focus { - border: 1px solid grey; + outline: none; + caret-color: ${({ theme }) => theme.notificationColor}; } `; diff --git a/packages/react-chat/src/components/Chat/ChatMessages.tsx b/packages/react-chat/src/components/Chat/ChatMessages.tsx index e9d383e..c03601a 100644 --- a/packages/react-chat/src/components/Chat/ChatMessages.tsx +++ b/packages/react-chat/src/components/Chat/ChatMessages.tsx @@ -1,13 +1,15 @@ import React, { useEffect, useRef } from "react"; import styled from "styled-components"; +import { Theme } from "../../styles/themes"; import { ChatMessage } from "../models/ChatMessage"; type ChatMessagesProps = { messages: ChatMessage[]; + theme: Theme; }; -export function ChatMessages({ messages }: ChatMessagesProps) { +export function ChatMessages({ messages, theme }: ChatMessagesProps) { const ref = useRef(null); useEffect(() => { if (ref && ref.current) { @@ -19,15 +21,15 @@ export function ChatMessages({ messages }: ChatMessagesProps) { {messages.map((message) => ( - - - + - {message.sender} - {message.date.toLocaleTimeString()} + {message.sender} + + {message.date.toLocaleTimeString()} + - {message.content} + {message.content} ))} @@ -35,64 +37,59 @@ export function ChatMessages({ messages }: ChatMessagesProps) { ); } -const MessageText = styled.div` - overflow-wrap: anywhere; +interface ThemeProps { + theme: Theme; +} + +const MessagesWrapper = styled.div` + height: calc(100% - 44px); + overflow: auto; + padding: 8px 16px 0; +`; + +const MessageWrapper = styled.div` width: 100%; -`; - -const MessageHeaderWrapper = styled.div` display: flex; -`; - -const TimeWrapper = styled.div` - font-family: Inter; - font-style: normal; - font-weight: normal; - font-size: 10px; - line-height: 14px; - letter-spacing: 0.2px; - text-transform: uppercase; - color: #939ba1; - - text-align: center; - margin-top: auto; - margin-bottom: auto; - margin-left: 4px; -`; - -const UserNameWrapper = styled.div` - font-family: Inter; - font-style: normal; - font-weight: 500; - font-size: 15px; - line-height: 22px; + align-items: center; + padding: 8px 0; + margin-bottom: 8px; `; const ContentWrapper = styled.div` display: flex; flex-direction: column; + margin-left: 8px; +`; + +const MessageHeaderWrapper = styled.div` + display: flex; + align-items: center; `; const Icon = styled.div` width: 40px; height: 40px; border-radius: 50%; - background-color: blue; + background-color: #bcbdff; `; -const IconWrapper = styled.div` - margin-left: 16px; - margin-right: 8px; +const UserNameWrapper = styled.div` + font-size: 15px; + line-height: 22px; + color: ${({ theme }) => theme.memberNameColor}; `; -const MessageWrapper = styled.div` +const TimeWrapper = styled.div` + font-size: 10px; + line-height: 14px; + letter-spacing: 0.2px; + text-transform: uppercase; + color: ${({ theme }) => theme.textSecondaryColor}; + margin-left: 4px; +`; + +const MessageText = styled.div` + overflow-wrap: anywhere; width: 100%; - display: flex; - margin-top: 8px; - margin-bottom: 8px; -`; - -const MessagesWrapper = styled.div` - height: calc(100% - 44px); - overflow: auto; + color: ${({ theme }) => theme.textPrimaryColor}; `; diff --git a/packages/react-chat/src/components/Members.tsx b/packages/react-chat/src/components/Members.tsx index 9c6e9aa..758a7ed 100644 --- a/packages/react-chat/src/components/Members.tsx +++ b/packages/react-chat/src/components/Members.tsx @@ -1,12 +1,18 @@ import React from "react"; import styled from "styled-components"; -export function Members() { - return ; +import { Theme } from "../styles/themes"; + +interface MembersProps { + theme: Theme; } -const MembersWrapper = styled.div` - width: 33%; +export function Members({ theme }: MembersProps) { + return Members; +} + +const MembersWrapper = styled.div` + width: 18%; height: 100%; background-color: ${({ theme }) => theme.sectionBackgroundColor}; `; diff --git a/packages/react-chat/src/components/ReactChat.tsx b/packages/react-chat/src/components/ReactChat.tsx index d9d25d3..1d68426 100644 --- a/packages/react-chat/src/components/ReactChat.tsx +++ b/packages/react-chat/src/components/ReactChat.tsx @@ -9,7 +9,7 @@ export function ReactChat() { return (
- +
); } diff --git a/packages/react-chat/src/helpers/channelsMock.ts b/packages/react-chat/src/helpers/channelsMock.ts new file mode 100644 index 0000000..c0f5070 --- /dev/null +++ b/packages/react-chat/src/helpers/channelsMock.ts @@ -0,0 +1,32 @@ +export type ChannelData = { + id: number; + name: string; + icon?: string; + members: number; + notifications?: number; +}; + +export const channels = [ + { + id: 1, + name: "welcome", + icon: "https://www.cryptokitties.co/icons/logo.svg", + members: 7, + }, + { + id: 2, + name: "general", + members: 15, + }, + { + id: 3, + name: "beginners", + members: 3, + notifications: 1, + }, + { + id: 4, + name: "random", + members: 6, + }, +]; diff --git a/packages/react-chat/src/styles/themes.ts b/packages/react-chat/src/styles/themes.ts index ad3b550..ee83d79 100644 --- a/packages/react-chat/src/styles/themes.ts +++ b/packages/react-chat/src/styles/themes.ts @@ -5,7 +5,11 @@ export type Theme = { sectionBackgroundColor: string; memberNameColor: string; guestNameColor: string; + iconColor: string; + iconTextColor: string; + activeChannelBackground: string; notificationColor: string; + inputColor: string; }; export const lightTheme: Theme = { @@ -15,7 +19,11 @@ export const lightTheme: Theme = { sectionBackgroundColor: "#F6F8FA", memberNameColor: "#4360DF", guestNameColor: "#887AF9", + iconColor: "#D37EF4", + iconTextColor: "rgba(255, 255, 255, 0.7)", + activeChannelBackground: "#E9EDF1", notificationColor: "#4360DF", + inputColor: "#EEF2F5", }; export const darkTheme: Theme = { @@ -25,7 +33,11 @@ export const darkTheme: Theme = { sectionBackgroundColor: "#252525", memberNameColor: "#88B0FF", guestNameColor: "#887AF9", + iconColor: "#D37EF4", + iconTextColor: "rgba(0, 0, 0, 0.7)", + activeChannelBackground: "#2C2C2C", notificationColor: "#887AF9", + inputColor: "#373737", }; export default { lightTheme, darkTheme };