From b3802660d4b5be123bb272f91802fa064ff10e77 Mon Sep 17 00:00:00 2001 From: Kechicode <186776112+Kechicode@users.noreply.github.com> Date: Fri, 17 Jan 2025 17:05:57 +0800 Subject: [PATCH 1/4] feat: Add dropdown toggle functionality to SingleLine component --- .../Home/Channel/DropdownDialog/index.tsx | 99 +++++++++++++++++++ .../Channel/DropdownDialog/styles.module.css | 52 ++++++++++ src/views/Home/Channel/SingleLine/index.tsx | 9 +- .../Home/Channel/SingleLine/styles.module.css | 10 ++ src/views/Home/Feed/MainFeed/index.tsx | 14 ++- 5 files changed, 182 insertions(+), 2 deletions(-) create mode 100644 src/views/Home/Channel/DropdownDialog/index.tsx create mode 100644 src/views/Home/Channel/DropdownDialog/styles.module.css diff --git a/src/views/Home/Channel/DropdownDialog/index.tsx b/src/views/Home/Channel/DropdownDialog/index.tsx new file mode 100644 index 0000000000..8997765d98 --- /dev/null +++ b/src/views/Home/Channel/DropdownDialog/index.tsx @@ -0,0 +1,99 @@ +import classnames from 'classnames' +import { useState } from 'react' +import { useEffect } from 'react' + +import { ReactComponent as IconUp } from '@/public/static/icons/24px/up.svg' +import { Icon } from '~/components' + +import styles from './styles.module.css' + +type DropdownDialogProps = { + items: { + id: string + title: string + link: string + }[] + toggleDropdown: () => void +} + +const DropdownDialog = ({ items, toggleDropdown }: DropdownDialogProps) => { + const [hash, setHash] = useState('') + + useEffect(() => { + // Function to update the hash state + const updateHash = () => { + setHash(window.location.hash) + } + + // Set the initial hash + updateHash() + + // Add an event listener to update the hash when it changes + window.addEventListener('hashchange', updateHash) + + // Clean up the event listener on component unmount + return () => { + window.removeEventListener('hashchange', updateHash) + } + }, []) + + const [selectedChannel, setSelectedChannel] = useState(1) + + useEffect(() => { + if (hash) { + const channel = parseInt(hash.split('=')[1], 10) + setSelectedChannel(channel) + } + }, [hash]) + + const handleClick = (event: React.MouseEvent) => { + // click in container but not in content + const target = event.target as HTMLElement + if (target.closest('#channel-dropdown-dialog-content') === null) { + toggleDropdown() + } + } + return ( +
+ +
+ ) +} + +export default DropdownDialog diff --git a/src/views/Home/Channel/DropdownDialog/styles.module.css b/src/views/Home/Channel/DropdownDialog/styles.module.css new file mode 100644 index 0000000000..61b1c6c93d --- /dev/null +++ b/src/views/Home/Channel/DropdownDialog/styles.module.css @@ -0,0 +1,52 @@ +.container { + position: fixed; + top: 66px; + right: 0; + left: 0; + z-index: 1000; + height: calc(100vh - 66px); + background-color: rgb(0 0 0 / 40%); +} + +.content { + padding: var(--sp8) var(--sp16) var(--sp20); + background-color: var(--color-white); + + & .header { + display: flex; + align-items: center; + justify-content: space-between; + + & .title { + font-size: var(--font-size-16); + font-weight: var(--font-semibold); + } + } + + & .body { + margin-top: var(--sp12); + + & .grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(76px, 1fr)); + gap: var(--sp12); + + & .gridItem { + display: flex; + align-items: center; + justify-content: center; + padding: var(--sp4) var(--sp10); + font-size: var(--font-size-14); + color: var(--color-grey-darker); + background-color: var(--color-grey-lighter); + border-radius: var(--sp8); + + &.selectedChannel { + font-weight: var(--font-semibold); + color: var(--color-white); + background-color: var(--color-black); + } + } + } + } +} diff --git a/src/views/Home/Channel/SingleLine/index.tsx b/src/views/Home/Channel/SingleLine/index.tsx index 7038f31893..c0b26e983f 100644 --- a/src/views/Home/Channel/SingleLine/index.tsx +++ b/src/views/Home/Channel/SingleLine/index.tsx @@ -2,6 +2,9 @@ import classnames from 'classnames' import { useState } from 'react' import { useEffect } from 'react' +import { ReactComponent as IconDown } from '@/public/static/icons/24px/down.svg' +import { Icon } from '~/components' + import styles from './styles.module.css' type SingleLineProps = { @@ -10,9 +13,10 @@ type SingleLineProps = { title: string link: string }[] + toggleDropdown: () => void } -const SingleLine = ({ items }: SingleLineProps) => { +const SingleLine = ({ items, toggleDropdown }: SingleLineProps) => { const [hash, setHash] = useState('') useEffect(() => { @@ -72,6 +76,9 @@ const SingleLine = ({ items }: SingleLineProps) => { {item.title} ))} + // ) diff --git a/src/views/Home/Channel/SingleLine/styles.module.css b/src/views/Home/Channel/SingleLine/styles.module.css index a102c8a937..7b24c5717c 100644 --- a/src/views/Home/Channel/SingleLine/styles.module.css +++ b/src/views/Home/Channel/SingleLine/styles.module.css @@ -41,3 +41,13 @@ } } } + +.moreBtn { + position: sticky; + right: 0; + z-index: 1; /* ensure button stays on top */ + padding: 8px 16px; /* expand button area */ + color: var(--color-grey-darker); + cursor: pointer; + background: var(--color-white); +} diff --git a/src/views/Home/Feed/MainFeed/index.tsx b/src/views/Home/Feed/MainFeed/index.tsx index 646d83891c..98e8154896 100644 --- a/src/views/Home/Feed/MainFeed/index.tsx +++ b/src/views/Home/Feed/MainFeed/index.tsx @@ -22,6 +22,7 @@ import { } from '~/gql/graphql' import Announcements from '../../Announcements' +import DropdownDialog from '../../Channel/DropdownDialog' import ChannelCarousel from '../../Channel/Page/Carousel' import SingleLine from '../../Channel/SingleLine' import Authors from '../Authors' @@ -237,6 +238,12 @@ const MainFeed = ({ feedSortType: sortBy }: MainFeedProps) => { }, ] + const [showDropdown, setShowDropdown] = useState(false) + + const toggleDropdown = () => { + setShowDropdown(!showDropdown) + } + const [showSingleLine, setShowSingleLine] = useState(false) useNativeEventListener('scroll', () => { @@ -376,7 +383,12 @@ const MainFeed = ({ feedSortType: sortBy }: MainFeedProps) => { - {showSingleLine && } + {showSingleLine && !showDropdown && ( + + )} + {showDropdown && ( + + )} {isHottestFeed && } {mixFeed.map((edge, i) => { From 487e4fbed3b4c49118f0bb6a2e36e22c56e24094 Mon Sep 17 00:00:00 2001 From: Kechicode <186776112+Kechicode@users.noreply.github.com> Date: Fri, 17 Jan 2025 20:09:55 +0800 Subject: [PATCH 2/4] Update styles.module.css --- src/views/Home/Channel/DropdownDialog/styles.module.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/views/Home/Channel/DropdownDialog/styles.module.css b/src/views/Home/Channel/DropdownDialog/styles.module.css index 61b1c6c93d..2efba12602 100644 --- a/src/views/Home/Channel/DropdownDialog/styles.module.css +++ b/src/views/Home/Channel/DropdownDialog/styles.module.css @@ -38,6 +38,7 @@ padding: var(--sp4) var(--sp10); font-size: var(--font-size-14); color: var(--color-grey-darker); + white-space: nowrap; background-color: var(--color-grey-lighter); border-radius: var(--sp8); From 0cb340b568163e6cd927e2901df84d4ecaeef47c Mon Sep 17 00:00:00 2001 From: Kechicode <186776112+Kechicode@users.noreply.github.com> Date: Mon, 20 Jan 2025 11:38:58 +0800 Subject: [PATCH 3/4] fix(MainFeed): Remove redundant condition in MainFeed Component --- src/views/Home/Feed/MainFeed/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/Home/Feed/MainFeed/index.tsx b/src/views/Home/Feed/MainFeed/index.tsx index 98e8154896..22135ae4ff 100644 --- a/src/views/Home/Feed/MainFeed/index.tsx +++ b/src/views/Home/Feed/MainFeed/index.tsx @@ -383,7 +383,7 @@ const MainFeed = ({ feedSortType: sortBy }: MainFeedProps) => { - {showSingleLine && !showDropdown && ( + {showSingleLine && ( )} {showDropdown && ( From 808de280c622937973f93ccceda8cd99373f584d Mon Sep 17 00:00:00 2001 From: Kechicode <186776112+Kechicode@users.noreply.github.com> Date: Mon, 20 Jan 2025 16:46:39 +0800 Subject: [PATCH 4/4] feat(Channel): Refactor CSS and Update Channel Titles --- .../Channel/DropdownDialog/styles.module.css | 17 ++++-- .../Channel/Page/Carousel/styles.module.css | 5 +- src/views/Home/Feed/MainFeed/index.tsx | 56 +++++++++---------- 3 files changed, 44 insertions(+), 34 deletions(-) diff --git a/src/views/Home/Channel/DropdownDialog/styles.module.css b/src/views/Home/Channel/DropdownDialog/styles.module.css index 2efba12602..5ac46f4dcd 100644 --- a/src/views/Home/Channel/DropdownDialog/styles.module.css +++ b/src/views/Home/Channel/DropdownDialog/styles.module.css @@ -32,13 +32,20 @@ gap: var(--sp12); & .gridItem { - display: flex; - align-items: center; - justify-content: center; + @mixin line-clamp; + padding: var(--sp4) var(--sp10); - font-size: var(--font-size-14); + font-size: var(--text14); color: var(--color-grey-darker); - white-space: nowrap; + + /* display: flex; */ + + /* align-items: center; */ + + /* justify-content: center; */ + text-align: center; + + /* white-space: nowrap; */ background-color: var(--color-grey-lighter); border-radius: var(--sp8); diff --git a/src/views/Home/Channel/Page/Carousel/styles.module.css b/src/views/Home/Channel/Page/Carousel/styles.module.css index 6740c61bc8..2449269d20 100644 --- a/src/views/Home/Channel/Page/Carousel/styles.module.css +++ b/src/views/Home/Channel/Page/Carousel/styles.module.css @@ -55,12 +55,15 @@ } & a { + @mixin line-clamp; + padding: var(--sp4) var(--sp10); font-size: var(--text14); line-height: 1.375rem; color: var(--color-grey-darker); text-align: center; - white-space: nowrap; + + /* white-space: nowrap; */ &.selectedChannel { font-weight: var(--font-semibold); diff --git a/src/views/Home/Feed/MainFeed/index.tsx b/src/views/Home/Feed/MainFeed/index.tsx index 22135ae4ff..8a8ce88780 100644 --- a/src/views/Home/Feed/MainFeed/index.tsx +++ b/src/views/Home/Feed/MainFeed/index.tsx @@ -98,142 +98,142 @@ const MainFeed = ({ feedSortType: sortBy }: MainFeedProps) => { const items = [ { id: '1', - title: 'Item 1', + title: '精選', link: `${host}/#channel=1`, }, { id: '2', - title: 'Item 2', + title: '時事話題', link: `${host}/#channel=2`, }, { id: '3', - title: 'Item 3', + title: '思想歷史', link: `${host}/#channel=3`, }, { id: '4', - title: 'Item 4', + title: '文化藝術', link: `${host}/#channel=4`, }, { id: '5', - title: 'Item 5', + title: '科技', link: `${host}/#channel=5`, }, { id: '6', - title: 'Item 6', + title: '經濟財經', link: `${host}/#channel=6`, }, { id: '7', - title: 'Item 7', + title: '政治', link: `${host}/#channel=7`, }, { id: '8', - title: 'Item 8', + title: '社會', link: `${host}/#channel=8`, }, { id: '9', - title: 'Item 9', + title: '國際', link: `${host}/#channel=9`, }, { id: '10', - title: 'Item 10', + title: '宗教', link: `${host}/#channel=10`, }, { id: '11', - title: 'Item 11', + title: '教育', link: `${host}/#channel=11`, }, { id: '12', - title: 'Item 12', + title: '法律', link: `${host}/#channel=12`, }, { id: '13', - title: 'Item 13', + title: '健康', link: `${host}/#channel=13`, }, { id: '14', - title: 'Item 14', + title: '運動', link: `${host}/#channel=14`, }, { id: '15', - title: 'Item 15', + title: '旅遊', link: `${host}/#channel=15`, }, { id: '16', - title: 'Item 16', + title: '娛樂', link: `${host}/#channel=16`, }, { id: '17', - title: 'Item 17', + title: '美食', link: `${host}/#channel=17`, }, { id: '18', - title: 'Item 18', + title: '旅居探索群', link: `${host}/#channel=18`, }, { id: '19', - title: 'Item 19', + title: '旅居探索分享', link: `${host}/#channel=19`, }, { id: '20', - title: 'Item 20', + title: '旅居探索', link: `${host}/#channel=20`, }, { id: '21', - title: 'Item 21', + title: 'Channel 21', link: `${host}/#channel=21`, }, { id: '22', - title: 'Item 22', + title: 'Channel 22', link: `${host}/#channel=22`, }, { id: '23', - title: 'Item 23', + title: 'Channel 23', link: `${host}/#channel=23`, }, { id: '24', - title: 'Item 24', + title: 'Channel 24', link: `${host}/#channel=24`, }, { id: '25', - title: 'Item 25', + title: 'Channel 25', link: `${host}/#channel=25`, }, { id: '26', - title: 'Item 26', + title: 'Channel 26', link: `${host}/#channel=26`, }, { id: '27', - title: 'Item 27', + title: 'Channel 27', link: `${host}/#channel=27`, }, { id: '28', - title: 'Item 28', + title: 'Channel 28', link: `${host}/#channel=28`, }, ]