diff --git a/public/icons/static/shape/about.svg b/public/icons/static/shape/about.svg new file mode 100644 index 000000000..30a25636b --- /dev/null +++ b/public/icons/static/shape/about.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/icons/static/shape/question.svg b/public/icons/static/shape/question.svg new file mode 100644 index 000000000..14cf8acf0 --- /dev/null +++ b/public/icons/static/shape/question.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/PromotionList/Holder.js b/src/components/PromotionList/Holder.js deleted file mode 100755 index fb6656c10..000000000 --- a/src/components/PromotionList/Holder.js +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react' - -import { ICON_CMD } from '@/config' - -import { - Wrapper, - BannerWrapper, - Logo, - PartnerInfo, - Title, - Desc, -} from './styles/holder' - -const Holder = () => ( - - - - 空缺中 - 联系我们 - - - - -) - -export default React.memo(Holder) diff --git a/src/components/PromotionList/PartnerBanner.js b/src/components/PromotionList/PartnerBanner.js deleted file mode 100755 index 872b730e0..000000000 --- a/src/components/PromotionList/PartnerBanner.js +++ /dev/null @@ -1,33 +0,0 @@ -import React from 'react' - -import { ICON_CMD } from '@/config' -import { - Wrapper, - ItemWrapper, - PartnerInfo, - Logo, - Title, - Desc, -} from './styles/partner_banner' - -const PartnerBanner = () => ( - - - - - 拉勾网 - 一个找工作的网站 - - - - - - - xaudiopro - 音频剪辑,格式转换 - - - -) - -export default React.memo(PartnerBanner) diff --git a/src/components/PromotionList/Spotlight.tsx b/src/components/PromotionList/Spotlight.tsx new file mode 100755 index 000000000..72e4cf763 --- /dev/null +++ b/src/components/PromotionList/Spotlight.tsx @@ -0,0 +1,29 @@ +import React from 'react' + +import type { TItem } from './spec' +import { + Wrapper, + ItemWrapper, + Header, + Logo, + Title, + Desc, +} from './styles/spotlight' + +type TProps = { + item: TItem +} + +const Spotlight: React.FC = ({ item }) => ( + + +
+ {item.title} + +
+ {item.desc} +
+
+) + +export default React.memo(Spotlight) diff --git a/src/components/PromotionList/WaitList.tsx b/src/components/PromotionList/WaitList.tsx new file mode 100644 index 000000000..2c28df092 --- /dev/null +++ b/src/components/PromotionList/WaitList.tsx @@ -0,0 +1,28 @@ +import React from 'react' + +import type { TItem } from './spec' +import { Wrapper, Item } from './styles/wait_list' + +type TProps = { + items: TItem[] + activeId: string + setActiveId: (id: string) => void +} + +const WaitList: React.FC = ({ items, activeId, setActiveId }) => { + return ( + + {items.map((item) => ( + setActiveId(item.id)} + > + {item.title} + + ))} + + ) +} + +export default React.memo(WaitList) diff --git a/src/components/PromotionList/fakeItems.ts b/src/components/PromotionList/fakeItems.ts new file mode 100644 index 000000000..9cfe6d4d2 --- /dev/null +++ b/src/components/PromotionList/fakeItems.ts @@ -0,0 +1,34 @@ +import { ICON_CMD } from '@/config' + +const items = [ + { + id: '1', + title: 'xaudiopro', + link: 'https://xxxx', + desc: '音频剪辑,格式转换, 音频剪辑,格式转换 音频剪辑', + cover: `${ICON_CMD}/test_ad.jpg`, + }, + { + id: '2', + title: 'github', + link: 'https://xxxx', + desc: 'github 就不用多介绍了吧', + cover: `${ICON_CMD}/test_ad.jpg`, + }, + { + id: '3', + title: 'xxoo', + link: 'https://xxxx', + desc: 'xxx 就不用多介绍了吧', + cover: `${ICON_CMD}/test_ad.jpg`, + }, + { + id: '4', + title: 'xxoo2', + link: 'https://xxxx', + desc: 'xxx2 就不用多介绍了吧', + cover: `${ICON_CMD}/test_ad.jpg`, + }, +] + +export default items diff --git a/src/components/PromotionList/index.js b/src/components/PromotionList/index.js deleted file mode 100755 index 4c5740793..000000000 --- a/src/components/PromotionList/index.js +++ /dev/null @@ -1,48 +0,0 @@ -/* - * - * PromotionList - * - */ - -import React from 'react' -import T from 'prop-types' - -import { ICON_CMD } from '@/config' -import { buildLog } from '@/utils' -import PartnerBanner from './PartnerBanner' -// import Holder from './Holder' - -import { Wrapper, Header, Title, AboutIcon } from './styles' - -/* eslint-disable-next-line */ -const log = buildLog('c:PromotionList:index') - -const PromotionList = ({ show, onAbout }) => { - return ( - - {show && ( - <> -
- 产品推广 -
- -
-
- - - )} -
- ) -} - -PromotionList.propTypes = { - show: T.bool, - onAbout: T.func, -} - -PromotionList.defaultProps = { - show: true, - onAbout: log, -} - -export default React.memo(PromotionList) diff --git a/src/components/PromotionList/index.tsx b/src/components/PromotionList/index.tsx new file mode 100755 index 000000000..f76a76a50 --- /dev/null +++ b/src/components/PromotionList/index.tsx @@ -0,0 +1,76 @@ +/* + * + * PromotionList + * + */ + +import React, { useState, useRef } from 'react' +import { findIndex } from 'ramda' + +import { ICON } from '@/config' +import { buildLog } from '@/utils' +import { useInterval } from '@/hooks' + +import useHoverDirty from 'react-use/lib/useHoverDirty' + +import type { TItem } from './spec' +import Spotlight from './Spotlight' +import WaitList from './WaitList' + +import { Wrapper, Header, Title, AboutIcon } from './styles' +import fakeItems from './fakeItems' + +/* eslint-disable-next-line */ +const log = buildLog('c:PromotionList:index') + +type TProps = { + show?: boolean + onAbout?: () => void + items: TItem[] + intervalSec?: number +} + +const PromotionList: React.FC = ({ + show = true, + onAbout = log, + items = fakeItems, + intervalSec = 5000, +}) => { + const [activeId, setActiveId] = useState(items[0].id) + const activeItemIndex = findIndex((item) => item.id === activeId, items) + const activeItem = items[activeItemIndex] + + const ref = useRef(null) + + const isHovering = useHoverDirty(ref) + + useInterval(() => { + if (isHovering) return + const nextIndex = + activeItemIndex < items.length - 1 ? activeItemIndex + 1 : 0 + setActiveId(items[nextIndex].id) + }, intervalSec) + + return ( + + {show && ( + <> +
+ 产品推广 +
+ +
+
+ + + + )} +
+ ) +} + +export default React.memo(PromotionList) diff --git a/src/components/PromotionList/spec.ts b/src/components/PromotionList/spec.ts new file mode 100644 index 000000000..9cc1f7d44 --- /dev/null +++ b/src/components/PromotionList/spec.ts @@ -0,0 +1,7 @@ +export type TItem = { + id: string + title: string + link: string + desc: string + cover?: string +} diff --git a/src/components/PromotionList/styles/holder.ts b/src/components/PromotionList/styles/holder.ts deleted file mode 100755 index c7720ed7f..000000000 --- a/src/components/PromotionList/styles/holder.ts +++ /dev/null @@ -1,39 +0,0 @@ -import styled from 'styled-components' - -import { theme, css } from '@/utils' -import Img from '@/Img' - -export const Wrapper = styled.div` - ${css.flex()}; -` -export const BannerWrapper = styled.div` - ${css.flex()}; - margin-bottom: 12px; -` -export const Logo = styled(Img)` - fill: ${theme('thread.articleDigest')}; - ${css.size(30)}; - opacity: 0; - ${BannerWrapper}:hover & { - opacity: 1; - } - transition: all 0.2s; - transition-delay: 0.2s; -` -export const PartnerInfo = styled.div` - ${css.flexColumn('justify-center', 'align-center')}; - margin-right: 40px; -` -export const Title = styled.div` - color: ${theme('thread.articleTitle')}; - font-size: 0.9rem; -` -export const Desc = styled.div` - color: ${theme('thread.articleDigest')}; - font-size: 0.8rem; - opacity: 0; - ${BannerWrapper}:hover & { - opacity: 1; - } - transition: all 0.2s; -` diff --git a/src/components/PromotionList/styles/index.ts b/src/components/PromotionList/styles/index.ts index 13d01afef..0d52c6c9f 100755 --- a/src/components/PromotionList/styles/index.ts +++ b/src/components/PromotionList/styles/index.ts @@ -5,30 +5,25 @@ import { css, theme } from '@/utils' export const Wrapper = styled.div` margin-left: 20px; - margin-top: 40px; + margin-top: 30px; min-height: 150px; max-width: 180px; ` export const Header = styled.div` ${css.flex('justify-between', 'align-center')}; + padding-top: 20px; margin-bottom: 12px; + border-top: 1px solid; + border-top-color: ${theme('thread.articleSpliter')}; + width: 80%; ` export const Title = styled.div` + font-size: 12px; color: ${theme('thread.articleDigest')}; - border-top: 1px solid; - border-top-color: ${theme('thread.articleSpliter')}; padding-top: 15px; - width: 80px; - - ${Wrapper}:hover & { - width: 100%; - } - - transition: width 0.3s; - transition-delay: 0.2s; ` export const AboutIcon = styled(Img)` - ${css.size(14)}; + ${css.size(15)}; fill: ${theme('thread.articleDigest')}; opacity: 0; margin-top: 16px; @@ -41,6 +36,5 @@ export const AboutIcon = styled(Img)` ${Wrapper}:hover & { opacity: 1; } - transition: all 0.2s; - transition-delay: 0.2s; + transition: all 0.25s; ` diff --git a/src/components/PromotionList/styles/partner_banner.ts b/src/components/PromotionList/styles/partner_banner.ts deleted file mode 100755 index 0f79c8550..000000000 --- a/src/components/PromotionList/styles/partner_banner.ts +++ /dev/null @@ -1,57 +0,0 @@ -import styled from 'styled-components' - -import { css, theme } from '@/utils' -import Img from '@/Img' - -export const Wrapper = styled.div` - ${css.flexColumn()}; -` -export const ItemWrapper = styled.div` - ${css.flex('align-center')}; - padding: 8px 0; - - &:hover { - cursor: pointer; - padding: 8px 5px; - border-radius: 4px; - } - transition: all 0.25s; - transition-delay: 0.2s; -` -export const Logo = styled(Img)` - ${css.size(20)}; - border-radius: 5px; - /* TODO: for dark themes */ - filter: saturate(0.8); - transition: all 0.25s; - transition-delay: 0.2s; - - ${ItemWrapper}:hover & { - width: 24px; - height: 24px; - } -` -export const PartnerInfo = styled.div` - ${css.flexColumn('justify-center')}; - margin-left: 10px; -` -export const Title = styled.div` - color: ${theme('thread.articleTitle')}; - font-size: 13px; - - ${ItemWrapper}:hover & { - font-weight: bold; - } -` -export const Desc = styled.div` - color: ${theme('thread.articleDigest')}; - font-size: 12px; - height: 1px; - overflow-y: hidden; - - ${ItemWrapper}:hover & { - height: 14px; - } - transition: all 0.25s; - transition-delay: 0.2s; -` diff --git a/src/components/PromotionList/styles/spotlight.ts b/src/components/PromotionList/styles/spotlight.ts new file mode 100755 index 000000000..31aab17f8 --- /dev/null +++ b/src/components/PromotionList/styles/spotlight.ts @@ -0,0 +1,37 @@ +import styled from 'styled-components' + +import { css, theme } from '@/utils' +import Img from '@/Img' + +export const Wrapper = styled.div` + ${css.flexColumn()}; + background: #06303b; + padding: 15px 10px; + width: 100%; + min-height: 120px; + border-radius: 5px; + margin-left: -12px; +` +export const ItemWrapper = styled.div` + ${css.flexColumn()}; +` +export const Header = styled.div` + ${css.flex('align-center', 'justify-between')}; + margin-left: 2px; +` +export const Logo = styled(Img)` + ${css.size(25)}; + border-radius: 5px; + margin-right: 10px; + /* TODO: for dark themes */ + filter: saturate(0.8); +` +export const Title = styled.div` + color: ${theme('thread.articleTitle')}; + font-size: 15px; +` +export const Desc = styled.div` + color: ${theme('thread.articleDigest')}; + font-size: 12px; + margin-top: 12px; +` diff --git a/src/components/PromotionList/styles/wait_list.ts b/src/components/PromotionList/styles/wait_list.ts new file mode 100644 index 000000000..aa5c0c9a0 --- /dev/null +++ b/src/components/PromotionList/styles/wait_list.ts @@ -0,0 +1,27 @@ +import styled from 'styled-components' + +import type { TActive } from '@/spec' +import { css, theme } from '@/utils' + +export const Wrapper = styled.div` + ${css.flex('align-center')}; + flex-wrap: wrap; + width: 100%; + margin-top: 10px; + margin-left: -2px; +` +export const Item = styled.div` + ${css.cutRest('50px')}; + color: ${({ active }) => + active ? theme('thread.articleTitle') : theme('thread.articleDigest')}; + font-size: 12px; + margin-right: 8px; + margin-bottom: 2px; + + &:hover { + cursor: pointer; + color: ${theme('thread.articleTitle')}; + } + + transition: all 0.3s; +` diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 1c0b6637c..44f925356 100755 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -11,5 +11,6 @@ export { default as useDevice } from './useDevice' export { default as useNetwork } from 'react-use/lib/useNetwork' export { default as useCopyToClipboard } from 'react-use/lib/useCopyToClipboard' +export { default as useInterval } from 'react-use/lib/useInterval' export { useSwipeable as useSwipe } from 'react-swipeable'