Skip to content
This repository was archived by the owner on Nov 8, 2022. It is now read-only.

Commit 13dafc3

Browse files
authored
refactor(help-center): design basic UI/UX (#1012)
* chore(redesign): peeklist basic ui/ux * fix(tags-bar): remove bottom sticky holder * fix(tags-bar): margin adjust * refactor: re-org tags bar && basic collapse menu comp * refactor: community banner, join -> member * refactor(help-center): basic page UI * chore(help-center): add size-limit config * chore(help-center): display real community info on banner * chore(help-center): adjust color on banner * refactor(help-center): basic cover/detail view UI/UX * refactor(help-center): use common LinksCard in FaqList
1 parent 20c0657 commit 13dafc3

File tree

48 files changed

+1237
-128
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1237
-128
lines changed

server/routes.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,11 @@ router.route('/discovery/:category').get((req, res) => {
119119
return renderAndCache({ req, res, path: '/discovery' })
120120
})
121121

122+
// 帮助中心
123+
router.route('/:community/help-center').get((req, res) => {
124+
return renderAndCache({ req, res, path: '/help-center' })
125+
})
126+
122127
// 社区主页
123128
router.route('/:community/:thread').get((req, res) => {
124129
if (

size-limit.config.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@
5151
{
5252
"path": ".next/server/pages/create/article.js",
5353
"maxSize": "280 kB"
54+
},
55+
{
56+
"path": ".next/server/pages/help-center.js",
57+
"maxSize": "280 kB"
5458
}
5559
],
5660
"ci": {
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
import React, { useState, useRef, useEffect } from 'react'
2+
import T from 'prop-types'
3+
4+
import { findIndex } from 'ramda'
5+
6+
import { ICON } from '@/config'
7+
import { buildLog } from '@/utils'
8+
9+
import Item from './Item'
10+
11+
import {
12+
Wrapper,
13+
TagsWrapper,
14+
Header,
15+
ArrowIcon,
16+
Title,
17+
Content,
18+
SubToggle,
19+
SubToggleTitle,
20+
SubTogglePrefixIcon,
21+
} from './styles/group'
22+
23+
/* eslint-disable-next-line */
24+
const log = buildLog('c:CollapseMenu:Group')
25+
26+
const Group = ({
27+
title,
28+
groupItems,
29+
items,
30+
activeItem,
31+
onSelect,
32+
maxDisplayCount,
33+
totalToggleThrold,
34+
}) => {
35+
// 决定是否显示 '展示更多' 的时候参考标签总数
36+
const needSubToggle =
37+
items?.length > totalToggleThrold && groupItems.length > maxDisplayCount
38+
39+
const initDisplayCount = needSubToggle ? maxDisplayCount : groupItems.length
40+
41+
const [isFolderOpen, toggleFolder] = useState(true)
42+
const [curDisplayCount, setCurDisplayCount] = useState(initDisplayCount)
43+
44+
const sortedItems = groupItems // sortByColor(groupItems)
45+
46+
const isActiveTagInFolder =
47+
findIndex((item) => item.id === activeItem.id, groupItems) >= 0
48+
49+
const subToggleRef = useRef(null)
50+
// 当选中的 Tag 被折叠在展示更多里面时,将其展开
51+
useEffect(() => {
52+
if (subToggleRef && isActiveTagInFolder) {
53+
setCurDisplayCount(groupItems.length)
54+
}
55+
}, [subToggleRef, isActiveTagInFolder, groupItems])
56+
57+
return (
58+
<Wrapper>
59+
<Header
60+
onClick={() => {
61+
toggleFolder(!isFolderOpen)
62+
63+
// 当关闭 Folder 的时候,如果当前 Folder 没有被激活的 Tag, 那么就回到折叠状态
64+
// 如果有,那么保持原来的状态
65+
if (isFolderOpen && !isActiveTagInFolder) {
66+
setCurDisplayCount(maxDisplayCount)
67+
}
68+
}}
69+
>
70+
<ArrowIcon
71+
isOpen={isFolderOpen}
72+
src={`${ICON}/shape/arrow-simple.svg`}
73+
/>
74+
<Title>{title}</Title>
75+
</Header>
76+
77+
<Content isOpen={isFolderOpen}>
78+
<TagsWrapper>
79+
{sortedItems.slice(0, curDisplayCount).map((item) => (
80+
<Item
81+
key={item.id}
82+
item={item}
83+
active={activeItem.id === item.id}
84+
activeId={activeItem.id}
85+
onSelect={onSelect}
86+
/>
87+
))}
88+
</TagsWrapper>
89+
{needSubToggle && (
90+
<SubToggle
91+
ref={subToggleRef}
92+
onClick={() => {
93+
setCurDisplayCount(
94+
curDisplayCount === maxDisplayCount
95+
? groupItems.length
96+
: maxDisplayCount,
97+
)
98+
}}
99+
>
100+
<SubTogglePrefixIcon src={`${ICON}/shape/more.svg`} />
101+
<SubToggleTitle>
102+
{curDisplayCount === maxDisplayCount ? '展开更多' : '收起'}
103+
</SubToggleTitle>
104+
</SubToggle>
105+
)}
106+
</Content>
107+
</Wrapper>
108+
)
109+
}
110+
111+
Group.propTypes = {
112+
// title, groupItems, items, activeItem, onSelect
113+
title: T.string,
114+
groupItems: T.arrayOf(
115+
T.shape({
116+
id: T.number,
117+
title: T.string,
118+
}),
119+
).isRequired,
120+
items: T.arrayOf(
121+
T.shape({
122+
id: T.number,
123+
title: T.string,
124+
}),
125+
).isRequired,
126+
activeItem: T.shape({
127+
id: T.number,
128+
title: T.string,
129+
}).isRequired,
130+
maxDisplayCount: T.number.isRequired,
131+
totalToggleThrold: T.number.isRequired,
132+
onSelect: T.func.isRequired,
133+
}
134+
135+
Group.defaultProps = {
136+
title: '',
137+
}
138+
139+
export default React.memo(Group)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import React from 'react'
2+
3+
import { Wrapper, Title } from './styles/item'
4+
5+
// const Item = ({ item, active, activeId, onSelect }) => {
6+
const Item = ({ item, active, onSelect }) => {
7+
return (
8+
<Wrapper active={active}>
9+
<Title active={active} onClick={() => onSelect(item)}>
10+
{item.title}
11+
</Title>
12+
</Wrapper>
13+
)
14+
}
15+
16+
export default Item
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/*
2+
*
3+
* CollapseMenu
4+
*
5+
*/
6+
7+
import React from 'react'
8+
import T from 'prop-types'
9+
import { keys } from 'ramda'
10+
11+
import { buildLog, groupByKey } from '@/utils'
12+
13+
import Group from './Group'
14+
15+
import { Wrapper } from './styles'
16+
17+
const MAX_DISPLAY_COUNT = 5
18+
const TOTAL_TOGGLE_THROLD = 8 // 15
19+
20+
/* eslint-disable-next-line */
21+
const log = buildLog('c:CollapseMenu:index')
22+
23+
const defaultActiveItem = { id: 2 }
24+
const defaultItems = [
25+
{
26+
id: 1,
27+
title: 'coderplanets 是什么?',
28+
group: '基础问答',
29+
},
30+
{
31+
id: 2,
32+
title: '持续部署项目实践',
33+
group: '基础问答',
34+
},
35+
{
36+
id: 3,
37+
title: 'coderplanets 是什么 3?',
38+
group: '基础问答',
39+
},
40+
41+
{
42+
id: 4,
43+
title: 'coderplanets 是什么 4?',
44+
group: '进阶问答',
45+
},
46+
{
47+
id: 5,
48+
title: 'coderplanets 是什么 5?',
49+
group: '进阶问答',
50+
},
51+
{
52+
id: 6,
53+
title: 'coderplanets 是什么 6?',
54+
group: '进阶问答',
55+
},
56+
{
57+
id: 7,
58+
title: 'coderplanets 是什么 7?',
59+
group: '进阶问答',
60+
},
61+
{
62+
id: 8,
63+
title: 'coderplanets 是什么 8?',
64+
group: '进阶问答',
65+
},
66+
{
67+
id: 9,
68+
title: 'coderplanets 是什么 9?',
69+
group: '进阶问答',
70+
},
71+
{
72+
id: 10,
73+
title: 'coderplanets 是什么 10?',
74+
group: '进阶问答',
75+
},
76+
]
77+
78+
const CollapseMenu = ({
79+
testId,
80+
items,
81+
activeItem,
82+
onSelect,
83+
maxDisplayCount,
84+
totalToggleThrold,
85+
}) => {
86+
const groupedItems = groupByKey(items, 'group')
87+
const groupsKeys = keys(groupedItems)
88+
89+
return (
90+
<Wrapper testId={testId}>
91+
{groupsKeys.map((groupKey) => (
92+
<Group
93+
key={groupKey}
94+
title={groupKey}
95+
items={items}
96+
groupItems={groupedItems[groupKey]}
97+
activeItem={activeItem}
98+
maxDisplayCount={maxDisplayCount}
99+
totalToggleThrold={totalToggleThrold}
100+
onSelect={onSelect}
101+
/>
102+
))}
103+
</Wrapper>
104+
)
105+
}
106+
107+
CollapseMenu.propTypes = {
108+
testId: T.string,
109+
items: T.arrayOf(
110+
T.shape({
111+
id: T.number,
112+
title: T.string,
113+
group: T.string,
114+
}),
115+
), // .isRequired,
116+
activeItem: T.shape({
117+
id: T.number,
118+
title: T.string,
119+
group: T.string,
120+
}), // .isRequired,
121+
maxDisplayCount: T.number,
122+
totalToggleThrold: T.number,
123+
onSelect: T.func,
124+
}
125+
126+
CollapseMenu.defaultProps = {
127+
testId: 'collapse-menu',
128+
items: defaultItems,
129+
activeItem: defaultActiveItem,
130+
// default display count in each group, the remaining part will be folded
131+
maxDisplayCount: MAX_DISPLAY_COUNT,
132+
// if items count < than this, will not be folded in each group
133+
totalToggleThrold: TOTAL_TOGGLE_THROLD,
134+
onSelect: console.log,
135+
}
136+
137+
export default React.memo(CollapseMenu)
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import styled from 'styled-components'
2+
3+
import Img from '@/Img'
4+
import { css, theme } from '@/utils'
5+
6+
export const Wrapper = styled.div``
7+
8+
export const TagsWrapper = styled.div``
9+
10+
export const Header = styled.div`
11+
${css.flex('align-center')};
12+
margin-bottom: 8px;
13+
margin-left: 3px;
14+
&:hover {
15+
cursor: pointer;
16+
/* opacity: 0.65; */
17+
}
18+
`
19+
export const ArrowIcon = styled(Img)`
20+
fill: ${theme('tags.text')};
21+
${css.size(16)};
22+
opacity: 0.5;
23+
transform: ${({ isOpen }) => (isOpen ? 'rotate(270deg)' : 'rotate(180deg)')};
24+
transition: transform 0.5s;
25+
${Header}:hover & {
26+
opacity: 0.65;
27+
}
28+
`
29+
export const Title = styled.div`
30+
color: ${theme('tags.text')};
31+
opacity: 0.5;
32+
margin-left: 4px;
33+
font-size: 14px;
34+
margin-right: 8px;
35+
${css.cutFrom('85px')};
36+
37+
${Header}:hover & {
38+
opacity: 0.65;
39+
}
40+
`
41+
export const Content = styled.div`
42+
display: ${({ isOpen }) => (isOpen ? 'block' : 'none')};
43+
width: 100%;
44+
margin-bottom: 15px;
45+
`
46+
export const SubToggle = styled.div`
47+
${css.flex('align-center')};
48+
margin-left: 5px;
49+
opacity: 0.5;
50+
51+
&:hover {
52+
opacity: 0.8;
53+
cursor: pointer;
54+
}
55+
`
56+
export const SubToggleTitle = styled.div`
57+
color: ${theme('tags.text')};
58+
font-size: 12px;
59+
margin-left: 5px;
60+
padding: 2px;
61+
border-radius: 5px;
62+
`
63+
export const SubTogglePrefixIcon = styled(Img)`
64+
fill: ${theme('tags.text')};
65+
${css.size(14)};
66+
transform: rotate(90deg);
67+
`

0 commit comments

Comments
 (0)