diff --git a/src/components/Layout/TopNavBar/styles.module.css b/src/components/Layout/TopNavBar/styles.module.css index 9f1a73c094..f6750ab847 100644 --- a/src/components/Layout/TopNavBar/styles.module.css +++ b/src/components/Layout/TopNavBar/styles.module.css @@ -1,6 +1,4 @@ .container { - @mixin border-bottom-grey-light; - position: sticky; top: 0; z-index: calc(var(--z-index-global-header) + 1); @@ -9,10 +7,12 @@ justify-content: space-between; padding: var(--sp20) var(--sp16); background-color: var(--color-white); - border-bottom-color: var(--color-grey-hover); @media (--sm-up) { + @mixin border-bottom-grey-light; + padding: var(--sp20) var(--sp32); + border-bottom-color: var(--color-grey-hover); } & .left { diff --git a/src/components/Layout/index.tsx b/src/components/Layout/index.tsx index 6c35416bcb..6fae6041ac 100644 --- a/src/components/Layout/index.tsx +++ b/src/components/Layout/index.tsx @@ -6,10 +6,10 @@ import { Head, Media, useRoute } from '~/components' import AuthHeader from './AuthHeader' import FixedMain from './FixedMain' import Header from './Header' -import NavBar from './NavBar' +// import NavBar from './NavBar' import Notice from './Notice' // import SideFooter from './SideFooter' -import SideNav from './SideNav' +// import SideNav from './SideNav' import Spacing from './Spacing' import styles from './styles.module.css' import { TopNavBar } from './TopNavBar' @@ -21,12 +21,12 @@ export const Layout: React.FC<{ children?: React.ReactNode }> & { AuthHeader: typeof AuthHeader Notice: typeof Notice } = ({ children }) => { - const { isInPath } = useRoute() - const isInDraftDetail = isInPath('ME_DRAFT_DETAIL') - const isInArticleDetail = isInPath('ARTICLE_DETAIL') - const isInArticleDetailHistory = isInPath('ARTICLE_DETAIL_HISTORY') - const isInMomentDetail = isInPath('MOMENT_DETAIL') - const isInMomentDetailEdit = isInPath('MOMENT_DETAIL_EDIT') + // const { isInPath } = useRoute() + // const isInDraftDetail = isInPath('ME_DRAFT_DETAIL') + // const isInArticleDetail = isInPath('ARTICLE_DETAIL') + // const isInArticleDetailHistory = isInPath('ARTICLE_DETAIL_HISTORY') + // const isInMomentDetail = isInPath('MOMENT_DETAIL') + // const isInMomentDetailEdit = isInPath('MOMENT_DETAIL_EDIT') return ( <> @@ -34,21 +34,21 @@ export const Layout: React.FC<{ children?: React.ReactNode }> & {
- {!isInArticleDetailHistory && ( + {/* {!isInArticleDetailHistory && ( - )} + )} */} {children}
- {!isInDraftDetail && + {/* {!isInDraftDetail && !isInArticleDetail && !isInArticleDetailHistory && !isInMomentDetail && @@ -58,7 +58,7 @@ export const Layout: React.FC<{ children?: React.ReactNode }> & { - )} + )} */} ) } diff --git a/src/components/Layout/styles.module.css b/src/components/Layout/styles.module.css index afbc47c27b..94eae6a909 100644 --- a/src/components/Layout/styles.module.css +++ b/src/components/Layout/styles.module.css @@ -23,8 +23,8 @@ @media (--sm-up) { display: grid; - grid-template-areas: 'left content'; - grid-template-columns: 3rem minmax(0, 100%); + grid-template-areas: 'content'; + grid-template-columns: minmax(0, 100%); grid-column-gap: 1.25rem; } @@ -41,7 +41,7 @@ } .sidenav { - @media (--sm-up) { + @media (--md-up) { grid-column: left; } diff --git a/src/views/Home/Channel/Page/Carousel/Dot.tsx b/src/views/Home/Channel/Page/Carousel/Dot.tsx new file mode 100644 index 0000000000..b176f92044 --- /dev/null +++ b/src/views/Home/Channel/Page/Carousel/Dot.tsx @@ -0,0 +1,29 @@ +import classnames from 'classnames' + +import styles from './styles.module.css' + +interface Props { + index: number + scroll: (index: number) => void + selected: boolean +} + +const Dot = ({ index, scroll, selected }: Props) => { + const dotClasses = classnames({ + [styles.dot]: true, + [styles.selected]: selected, + }) + + return ( + <> +
scroll(index)} + /> + + ) +} + +export default Dot diff --git a/src/views/Home/Channel/Page/Carousel/index.tsx b/src/views/Home/Channel/Page/Carousel/index.tsx new file mode 100644 index 0000000000..1df7611b24 --- /dev/null +++ b/src/views/Home/Channel/Page/Carousel/index.tsx @@ -0,0 +1,327 @@ +import classnames from 'classnames' +import useEmblaCarousel from 'embla-carousel-react' +import { + type MouseEvent, + // useCallback, + useEffect, + useRef, + useState, +} from 'react' + +import { useNativeEventListener } from '~/components' + +// import { useCarousel } from '~/components/Hook/useCarousel' +import Dot from './Dot' +import styles from './styles.module.css' + +type ColumnCount = '4' | '5' | '6' | '7' + +const ChannelCarousel = () => { + const host = typeof window !== 'undefined' ? window.location.origin : '' + const items = [ + { + id: '1', + title: 'Item 1', + link: `${host}/#channel=1`, + }, + { + id: '2', + title: 'Item 2', + link: `${host}/#channel=2`, + }, + { + id: '3', + title: 'Item 3', + link: `${host}/#channel=3`, + }, + { + id: '4', + title: 'Item 4', + link: `${host}/#channel=4`, + }, + { + id: '5', + title: 'Item 5', + link: `${host}/#channel=5`, + }, + { + id: '6', + title: 'Item 6', + link: `${host}/#channel=6`, + }, + { + id: '7', + title: 'Item 7', + link: `${host}/#channel=7`, + }, + { + id: '8', + title: 'Item 8', + link: `${host}/#channel=8`, + }, + { + id: '9', + title: 'Item 9', + link: `${host}/#channel=9`, + }, + { + id: '10', + title: 'Item 10', + link: `${host}/#channel=10`, + }, + { + id: '11', + title: 'Item 11', + link: `${host}/#channel=11`, + }, + { + id: '12', + title: 'Item 12', + link: `${host}/#channel=12`, + }, + { + id: '13', + title: 'Item 13', + link: `${host}/#channel=13`, + }, + { + id: '14', + title: 'Item 14', + link: `${host}/#channel=14`, + }, + { + id: '15', + title: 'Item 15', + link: `${host}/#channel=15`, + }, + { + id: '16', + title: 'Item 16', + link: `${host}/#channel=16`, + }, + { + id: '17', + title: 'Item 17', + link: `${host}/#channel=17`, + }, + { + id: '18', + title: 'Item 18', + link: `${host}/#channel=18`, + }, + { + id: '19', + title: 'Item 19', + link: `${host}/#channel=19`, + }, + { + id: '20', + title: 'Item 20', + link: `${host}/#channel=20`, + }, + { + id: '21', + title: 'Item 21', + link: `${host}/#channel=21`, + }, + { + id: '22', + title: 'Item 22', + link: `${host}/#channel=22`, + }, + { + id: '23', + title: 'Item 23', + link: `${host}/#channel=23`, + }, + { + id: '24', + title: 'Item 24', + link: `${host}/#channel=24`, + }, + { + id: '25', + title: 'Item 25', + link: `${host}/#channel=25`, + }, + { + id: '26', + title: 'Item 26', + link: `${host}/#channel=26`, + }, + { + id: '27', + title: 'Item 27', + link: `${host}/#channel=27`, + }, + { + id: '28', + title: 'Item 28', + link: `${host}/#channel=28`, + }, + ] + const [dot, setDot] = useState(0) + const [, setSnaps] = useState([]) + const [carousel, carouselApi] = useEmblaCarousel({ + loop: true, + skipSnaps: false, + }) + + const [columnCount, setColumnCount] = useState('4') + + useNativeEventListener('resize', () => { + if (typeof window !== 'undefined') { + if (window.innerWidth < 453) { + setColumnCount('4') + } + if (window.innerWidth >= 453) { + setColumnCount('5') + } + if (window.innerWidth >= 561) { + setColumnCount('6') + } + if (window.innerWidth >= 669) { + setColumnCount('7') + } + } + }) + + const [slicedItems, setSlicedItems] = useState([]) + + useEffect(() => { + const pageCount = Math.ceil(items.length / (Number(columnCount) * 2)) + const _slicedItems: any[] = [] + for (let i = 0; i < pageCount; i++) { + _slicedItems.push( + items.slice( + i * (Number(columnCount) * 2), + (i + 1) * (Number(columnCount) * 2) + ) + ) + } + setSlicedItems(_slicedItems) + }, [columnCount]) + + // state of carusel + const scrolling = useRef(false) + + const scroll = (index: number) => { + if (!carouselApi) { + return + } + setDot(index) + carouselApi.scrollTo(index) + stop() + } + + const onSelect = () => { + if (carouselApi) { + setDot(carouselApi.selectedScrollSnap()) + } + } + + const onCaptureClick = (event: MouseEvent) => { + if (scrolling.current) { + event.preventDefault() + event.stopPropagation() + } + } + + useEffect(() => { + if (!carouselApi) { + return + } + + carouselApi.reInit() + carouselApi.scrollTo(0) + + setDot(0) + setSnaps(carouselApi.scrollSnapList()) + + carouselApi.on('select', onSelect) + }, [carouselApi]) + + 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]) + + return ( +
+
+
+ {slicedItems?.map((its, i) => { + return ( +
+
+ {its?.map( + ( + item: { title: string; id: string; link: string }, + index: number + ) => { + const title = item.title || '' + return ( + + {title} + + ) + } + )} +
+
+ ) + })} +
+
+
+
+ {slicedItems?.map((_, index) => ( + + ))} +
+
+
+ ) +} + +export default ChannelCarousel diff --git a/src/views/Home/Channel/Page/Carousel/styles.module.css b/src/views/Home/Channel/Page/Carousel/styles.module.css new file mode 100644 index 0000000000..6740c61bc8 --- /dev/null +++ b/src/views/Home/Channel/Page/Carousel/styles.module.css @@ -0,0 +1,96 @@ +.carousel { + position: relative; + width: 100%; + @media (--sm-up) { + padding: 0; + } +} + +.footer { + @mixin flex-center-all; + + z-index: 1; + margin: var(--sp16) 0 var(--sp20); + + & .dots { + @mixin flex-center-start; + + padding-left: var(--sp8); + } +} + +.viewport { + width: 100%; + overflow: hidden; + + & .container { + display: flex; + margin-left: calc(var(--sp8) * -1); + } + + & .slide { + position: relative; + min-width: 100%; + } + + & .content { + position: relative; + z-index: 0; + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: var(--sp8) var(--sp12); + padding-left: var(--sp10); + border-radius: var(--sp16); + + @media (width >= 453px) { + grid-template-columns: repeat(5, 1fr); + } + + @media (width >= 561px) { + grid-template-columns: repeat(6, 1fr); + } + + @media (width >= 669px) { + grid-template-columns: repeat(7, 1fr); + } + + & a { + padding: var(--sp4) var(--sp10); + font-size: var(--text14); + line-height: 1.375rem; + color: var(--color-grey-darker); + text-align: center; + white-space: nowrap; + + &.selectedChannel { + font-weight: var(--font-semibold); + color: var(--color-white); + background: var(--color-black); + border-radius: var(--sp8); + } + } + + & p { + display: none; + } + } +} + +.dot { + @mixin transition; + + box-sizing: content-box; + box-sizing: border-box; + width: 12px; + height: 3px; + margin-right: var(--sp8); + cursor: pointer; + background: var(--color-grey); + border: 2px solid rgb(255 255 255 / 0%); + border-radius: var(--sp8); + transition-property: border-color, border-width, background-color; + + &.selected { + border-color: var(--color-black); + } +} diff --git a/src/views/Home/Feed/MainFeed/index.tsx b/src/views/Home/Feed/MainFeed/index.tsx index 69ba142fa7..74a9aa0056 100644 --- a/src/views/Home/Feed/MainFeed/index.tsx +++ b/src/views/Home/Feed/MainFeed/index.tsx @@ -21,6 +21,7 @@ import { } from '~/gql/graphql' import Announcements from '../../Announcements' +import ChannelCarousel from '../../Channel/Page/Carousel' import Authors from '../Authors' import Billboard from '../Billboard' import { FEED_ARTICLES_PRIVATE, FEED_ARTICLES_PUBLIC } from '../gql' @@ -215,6 +216,9 @@ const MainFeed = ({ feedSortType: sortBy }: MainFeedProps) => { eof > + + + {isHottestFeed && } {mixFeed.map((edge, i) => { if (edge?.__typename === 'HorizontalFeed') {