-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0127c15
commit 26bbc76
Showing
9 changed files
with
351 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
.van-index-anchor { | ||
padding: 0 16px; | ||
padding: var(--index-anchor-padding, 0 16px); | ||
color: #323233; | ||
color: var(--index-anchor-text-color, #323233); | ||
font-weight: 500; | ||
font-weight: var(--index-anchor-font-weight, 500); | ||
font-size: 14px; | ||
font-size: var(--index-anchor-font-size, 14px); | ||
line-height: 32px; | ||
line-height: var(--index-anchor-line-height, 32px); | ||
background-color: transparent; | ||
background-color: var(--index-anchor-background-color, transparent); | ||
} | ||
.van-index-anchor--active { | ||
right: 0; | ||
left: 0; | ||
color: #07c160; | ||
color: var(--index-anchor-active-text-color, #07c160); | ||
background-color: #fff; | ||
background-color: var(--index-anchor-active-background-color, #fff); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
// packages | ||
import React, { FunctionComponent, useContext, useMemo } from 'react'; | ||
import clsx from 'clsx'; | ||
import { View } from 'remax/wechat'; | ||
import { useNativeEffect } from 'remax'; | ||
// internal | ||
import IndexBarContext from '../IndexBar/IndexBarContext'; | ||
import './IndexAnchor.css'; | ||
|
||
// 默认值填充属性 | ||
interface IndexAnchorProps { | ||
// 容器类名,用以覆盖内部 | ||
className?: string; | ||
// 索引字符 | ||
index: string | number; | ||
} | ||
|
||
// TODO - adapt stickyOffsetTop | ||
const IndexAnchor: FunctionComponent<IndexAnchorProps> = (props) => { | ||
// index-bar relation | ||
const { | ||
activeAnchorIndex, | ||
scrollTop, | ||
wrapperStyle, | ||
anchorStyle, | ||
} = useContext(IndexBarContext); | ||
const { className, index } = props; | ||
const classnames = { | ||
container: clsx(className, 'van-index-anchor-wrapper'), | ||
anchor: clsx('van-index-anchor', { | ||
'van-index-anchor--active': activeAnchorIndex === index, | ||
'van-hairline--bottom': activeAnchorIndex === index, | ||
}), | ||
}; | ||
|
||
// 新增属性,便于获取实际节点 | ||
const id = useMemo(() => `van-index-anchor-wrapper-${index}`, [index]); | ||
|
||
useNativeEffect(() => { | ||
wx.createSelectorQuery() | ||
.select(`#${id}`) | ||
.boundingClientRect() | ||
.exec(([rect]: [WechatMiniprogram.BoundingClientRectResult]) => { | ||
if (activeAnchorIndex === index) { | ||
wx.pageScrollTo({ | ||
// duration: 0, | ||
scrollTop: scrollTop + rect.top, | ||
}); | ||
} | ||
}); | ||
}, [activeAnchorIndex]); | ||
|
||
return ( | ||
<View id={id} style={wrapperStyle} className={classnames.container}> | ||
<View style={anchorStyle} className={classnames.anchor}> | ||
{index} | ||
</View> | ||
</View> | ||
); | ||
}; | ||
|
||
export default IndexAnchor; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
/** | ||
* @description - nothing but export component | ||
*/ | ||
export { default } from './IndexAnchor'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
.van-index-bar { | ||
position: relative; | ||
} | ||
.van-index-bar__sidebar { | ||
position: fixed; | ||
top: 50%; | ||
right: 0; | ||
display: flex; | ||
flex-direction: column; | ||
text-align: center; | ||
transform: translateY(-50%); | ||
user-select: none; | ||
} | ||
.van-index-bar__index { | ||
font-weight: 500; | ||
padding: 0 4px 0 16px; | ||
padding: 0 var(--padding-base, 4px) 0 var(--padding-md, 16px); | ||
font-size: 10px; | ||
font-size: var(--index-bar-index-font-size, 10px); | ||
line-height: 14px; | ||
line-height: var(--index-bar-index-line-height, 14px); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
// packages | ||
import React, { FunctionComponent, useRef, useState } from 'react'; | ||
import clsx from 'clsx'; | ||
import { useNativeEffect } from 'remax'; | ||
import { View } from 'remax/wechat'; | ||
import { usePageEvent } from 'remax/macro'; | ||
// internal | ||
import IndexBarContext, { IndexBarContextPayload } from './IndexBarContext'; | ||
import withDefaultProps from '../tools/with-default-props-advance'; | ||
import pickStyle from '../tools/pick-style'; | ||
import './IndexBar.css'; | ||
|
||
// 默认值填充属性 | ||
interface NeutralIndexBarProps { | ||
indexList: string[]; | ||
zIndex: number; | ||
sticky: boolean; | ||
stickyOffsetTop: number; | ||
highlightColor: string; | ||
} | ||
|
||
interface ExogenousIndexBarProps { | ||
// 容器类名,用以覆盖内部 | ||
className?: string; | ||
// 选择字符回调 | ||
onSelect?: (event: { detail: string }) => void; | ||
} | ||
|
||
type IndexBarProps = NeutralIndexBarProps & ExogenousIndexBarProps; | ||
|
||
const DefaultIndexBarProps: NeutralIndexBarProps = { | ||
indexList: 'A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z'.split(','), | ||
zIndex: 1, | ||
sticky: true, | ||
stickyOffsetTop: 0, | ||
highlightColor: '#07c160', | ||
}; | ||
|
||
// TODO - support sticky anchor | ||
// TODO - support sync index from page scroll to sidebar | ||
const IndexBar: FunctionComponent<IndexBarProps> = (props) => { | ||
const { | ||
className, | ||
indexList, | ||
zIndex, | ||
highlightColor, | ||
children, | ||
onSelect, | ||
} = props; | ||
const classnames = { | ||
container: clsx(className, 'van-index-bar'), | ||
}; | ||
|
||
// continuous track scrollTop, weired, but better way not found | ||
const scrollTop = useRef(0); | ||
|
||
usePageEvent('onPageScroll', (event: { scrollTop: number }) => { | ||
scrollTop.current = event.scrollTop; | ||
}); | ||
|
||
const [activeAnchorIndex, setActiveAnchorIndex] = useState(''); | ||
// context 穿透 | ||
const payload: IndexBarContextPayload = { | ||
activeAnchorIndex, | ||
scrollTop: scrollTop.current, | ||
}; | ||
// 事件冒泡 | ||
const onSelectWrap = (event: any) => { | ||
const { index } = event.target.dataset; | ||
|
||
// 冒泡 | ||
if (typeof onSelect === 'function') { | ||
onSelect({ detail: index }); | ||
} | ||
|
||
// 滚动 | ||
setActiveAnchorIndex(index); | ||
}; | ||
|
||
// 计算滚动阶段 active anchor index | ||
const sidebar = useRef({ | ||
height: 0, | ||
top: 0, | ||
}); | ||
|
||
// TODO - support multiple sidebar within page, selector unique | ||
useNativeEffect(() => { | ||
wx.createSelectorQuery() | ||
.select('.van-index-bar__sidebar') | ||
.boundingClientRect() | ||
.exec(([rect]: [WechatMiniprogram.BoundingClientRectResult]) => { | ||
sidebar.current.height = rect.height; | ||
sidebar.current.top = rect.top; | ||
}); | ||
}, [indexList]); | ||
|
||
const onTouchMove = ({ touches: [touch] }: any) => { | ||
const { length } = indexList; | ||
const space = sidebar.current.height / length; | ||
const theory = { | ||
index: Math.floor((touch.clientY - sidebar.current.top) / space), | ||
}; | ||
|
||
if (theory.index < 0) { | ||
theory.index = 0; | ||
} else if (theory.index > length - 1) { | ||
theory.index = length - 1; | ||
} | ||
|
||
// convert serial number into actual index | ||
setActiveAnchorIndex(indexList[theory.index]); | ||
}; | ||
|
||
return ( | ||
<IndexBarContext.Provider value={payload}> | ||
<View className={classnames.container}> | ||
{children} | ||
<View | ||
className="van-index-bar__sidebar" | ||
onClick={onSelectWrap} | ||
onTouchMove={onTouchMove} | ||
> | ||
{indexList | ||
.map((index) => ({ | ||
index, | ||
style: pickStyle({ | ||
zIndex: zIndex + 1, | ||
color: activeAnchorIndex === index ? highlightColor : '', | ||
}), | ||
})) | ||
.map((item) => ( | ||
<View | ||
className="van-index-bar__index" | ||
key={item.index} | ||
style={item.style} | ||
data-index={item.index} | ||
> | ||
{item.index} | ||
</View> | ||
))} | ||
</View> | ||
</View> | ||
</IndexBarContext.Provider> | ||
); | ||
}; | ||
|
||
export default withDefaultProps<ExogenousIndexBarProps, NeutralIndexBarProps>( | ||
DefaultIndexBarProps | ||
)(IndexBar); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// packages | ||
import { CSSProperties, createContext } from 'react'; | ||
|
||
export interface IndexBarContextPayload { | ||
scrollTop: number; | ||
activeAnchorIndex: string | number; | ||
wrapperStyle?: CSSProperties; | ||
anchorStyle?: CSSProperties; | ||
} | ||
|
||
const IndexBarContext = createContext<IndexBarContextPayload>({ | ||
// none anchor selected | ||
scrollTop: 0, | ||
activeAnchorIndex: '', | ||
}); | ||
|
||
export default IndexBarContext; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
/** | ||
* @description - nothing but export component | ||
*/ | ||
export { default } from './IndexBar'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
// packages | ||
import * as React from 'react'; | ||
import { View, Text } from 'remax/wechat'; | ||
|
||
// internal | ||
import IndexBar from '../../../packages/IndexBar'; | ||
import IndexAnchor from '../../../packages/IndexAnchor'; | ||
import Cell from '../../../packages/Cell'; | ||
|
||
export default () => { | ||
return ( | ||
<View className="demo-block"> | ||
<Text className="demo-block__title">基础用法</Text> | ||
<IndexBar> | ||
<View> | ||
<IndexAnchor index="A">标题 1</IndexAnchor> | ||
<Cell title="文本" /> | ||
<Cell title="文本" /> | ||
<Cell title="文本" /> | ||
</View> | ||
<View> | ||
<IndexAnchor index="B">标题 1</IndexAnchor> | ||
<Cell title="文本" /> | ||
<Cell title="文本" /> | ||
<Cell title="文本" /> | ||
</View> | ||
<View> | ||
<IndexAnchor index="C">标题 1</IndexAnchor> | ||
<Cell title="文本" /> | ||
<Cell title="文本" /> | ||
<Cell title="文本" /> | ||
</View> | ||
<View> | ||
<IndexAnchor index="D">标题 1</IndexAnchor> | ||
<Cell title="文本" /> | ||
<Cell title="文本" /> | ||
<Cell title="文本" /> | ||
<Cell title="文本" /> | ||
<Cell title="文本" /> | ||
<Cell title="文本" /> | ||
</View> | ||
<View> | ||
<IndexAnchor index="F">标题 1</IndexAnchor> | ||
<Cell title="文本" /> | ||
<Cell title="文本" /> | ||
<Cell title="文本" /> | ||
</View> | ||
<View> | ||
<IndexAnchor index="H">标题 1</IndexAnchor> | ||
<Cell title="文本" /> | ||
<Cell title="文本" /> | ||
<Cell title="文本" /> | ||
</View> | ||
<View> | ||
<IndexAnchor index="W">标题 1</IndexAnchor> | ||
<Cell title="文本" /> | ||
<Cell title="文本" /> | ||
<Cell title="文本" /> | ||
</View> | ||
<View> | ||
<IndexAnchor index="Z">标题 1</IndexAnchor> | ||
<Cell title="文本" /> | ||
<Cell title="文本" /> | ||
<Cell title="文本" /> | ||
<Cell title="文本" /> | ||
</View> | ||
</IndexBar> | ||
</View> | ||
); | ||
}; |