Feat: IndexBar demo optimize (#1259)

* feat: google search console验证

* feat: indexBar受控模式优化

* feat: indexBar受控模式优化

* feat: indexBar受控模式优化
This commit is contained in:
homi 2024-08-06 14:38:18 +08:00 committed by GitHub
parent 4a4b721110
commit 2e1a556858
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 320 additions and 73 deletions

View File

@ -99,7 +99,8 @@
"pages/TabsElevator/index",
"pages/IndexBarScrollView/index",
"pages/SelectorFilter/index",
"pages/Typography/index"
"pages/Typography/index",
"pages/IndexBarControl/index"
],
"window": {
"enableWK": "YES",

View File

@ -0,0 +1,9 @@
.base {
width: 100%;
height: 100vh;
}
.indexbar {
position: fixed;
right: 10px;
top: 20vh;
}

View File

@ -0,0 +1,23 @@
<view class="base">
<index-bar
className="indexbar"
items="{{items}}"
current="{{current}}"
onChange="onChange"
>
<view
slot="$default"
slot-scope="props"
>
<list header="{{props.value.label}}">
<list-item
a:for="{{nameMap[props.value.label]}}"
a:for-index="index"
a:for-item="itemX"
>
{{itemX}}
</list-item>
</list>
</view>
</index-bar>
</view>

View File

@ -0,0 +1,47 @@
const nameMap = {
'A': ['Alice', 'Andy', 'Amanda'],
'B': ['Bob', 'Brian', 'Bella'],
'C': ['Cathy', 'Carl', 'Chris'],
'D': ['David', 'Diana', 'Derek'],
'E': ['Eva', 'Evan', 'Eddie'],
'F': ['Fiona', 'Frank', 'Fred'],
'G': ['George', 'Grace', 'Gavin'],
'H': ['Helen', 'Hank', 'Harry'],
'I': ['Ivy', 'Ian', 'Isaac'],
'J': ['Jack', 'Jill', 'James'],
'K': ['Karen', 'Kyle', 'Kurt'],
'L': ['Laura', 'Liam', 'Leo'],
'M': ['Megan', 'Mike', 'Mona'],
'N': ['Nina', 'Nick', 'Nancy'],
'O': ['Olivia', 'Oscar', 'Owen'],
'P': ['Paul', 'Pam', 'Pete'],
'Q': ['Quincy', 'Quinn', 'Queen'],
'R': ['Rachel', 'Ryan', 'Rita'],
'S': ['Sam', 'Sara', 'Steve'],
'T': ['Tom', 'Tina', 'Tim'],
'U': ['Uma', 'Ulysses', 'Ursula'],
'V': ['Violet', 'Victor', 'Vince'],
'W': ['Wendy', 'Will', 'Wanda'],
'X': ['Xander', 'Xenia', 'Xavier'],
'Y': ['Yara', 'Yuri', 'Yvonne'],
'Z': ['Zoe', 'Zach', 'Zane'],
};
Page({
data: {
nameMap,
items: [],
current: 'S',
},
onLoad() {
this.setData({
items: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('').map((u) => {
return { label: u, disablePreview: true };
}),
});
},
onChange(item, index) {
console.log(item, index);
this.setData({ current: item.label });
},
});

View File

@ -0,0 +1,11 @@
{
"defaultTitle": "IndexBar",
"pullRefresh": "NO",
"showProgress": "NO",
"allowsBounceVertical": "NO",
"usingComponents": {
"index-bar": "../../../src/IndexBar/index",
"list": "../../../src/List/index",
"list-item": "../../../src/List/ListItem/index"
}
}

View File

@ -2,7 +2,7 @@
<index-bar
className="indexbar"
items="{{items}}"
defaultCurrent="A"
defaultCurrent="H"
onChange="onChange"
>
<view

View File

@ -92,6 +92,11 @@ export const componentList = [
nameZN: '索引',
path: '/pages/IndexBarScrollView/index',
},
{
name: 'IndexBarControl',
nameZN: '索引-控制',
path: '/pages/IndexBarControl/index',
},
{
name: 'Collapse',
nameZN: '折叠面板',

View File

@ -29,7 +29,7 @@
{{item.label}}
</view>
<view
a:if="{{item.label === touchKey && !item.disablePreview}}"
a:if="{{item.label === touchKey && !item.disablePreview && moving}}"
class="ant-indexbar-side-item-tip"
>
{{item.label}}

View File

@ -20,27 +20,35 @@ toc: 'content'
## 代码示例
### 基本使用
<code src='pages/IndexBar/index'></code>
### 结合列表使用
<code src='pages/IndexBarScrollView/index'></code>
### 控制选中状态
<code src='pages/IndexBarControl/index'></code>
## API
| 属性 | 说明 | 类型 | 默认值 |
| ---- | ---- | ---- | ------ |
| activeClassName | 索引激活时的样式 | string | - |
| className | 类名 | string | - |
| current | 索引值 | string | - |
| defaultCurrent | 默认索引 | string | - |
| labelPreview | 索引预览内容,接收 value 和 index | slot | - |
| items | 索引数组 | [Item](#item) | [] |
| style | 样式 | string | - |
| size | 索引的尺寸(宽高,单位 px) | number | 16 |
| vibrate | 索引改变时是否震动 | boolean | true |
| onChange | 索引改变时的回调 | (value: [Item](#item), index: number) => void |
| 属性 | 说明 | 类型 | 默认值 |
| --------------- | --------------------------------- | --------------------------------------------- | ------ |
| activeClassName | 索引激活时的样式 | string | - |
| className | 类名 | string | - |
| current | 索引值 | string | - |
| defaultCurrent | 默认索引 | string | - |
| labelPreview | 索引预览内容,接收 value 和 index | slot | - |
| items | 索引数组 | [Item](#item) | [] |
| style | 样式 | string | - |
| size | 索引的尺寸(宽高,单位 px) | number | 16 |
| vibrate | 索引改变时是否震动 | boolean | true |
| onChange | 索引改变时的回调 | (value: [Item](#item), index: number) => void |
#### Item
| 属性 | 说明 | 类型 | 默认值 |
| ---- | ---- | ---- | ------ |
| label | 索引标识 | string | - |
| disablePreview | 禁用索引触发时的预览效果 | boolean | - |
| 属性 | 说明 | 类型 | 默认值 |
| -------------- | ------------------------ | ------- | ------ |
| label | 索引标识 | string | - |
| disablePreview | 禁用索引触发时的预览效果 | boolean | - |

View File

@ -1,5 +1,4 @@
import { IndexBarDefaultProps } from './props';
import '../_util/assert-component2';
Component({
props: IndexBarDefaultProps,
@ -15,20 +14,39 @@ Component({
hasDefaultSlot: true,
},
didMount() {
const { defaultCurrent, items } = this.props;
const { defaultCurrent, current, items } = this.props;
this.initItemHeight();
this.initTopRange();
const _index = items.findIndex((u) => defaultCurrent === u.label);
this.setData({ currentKey: _index });
const initCurrent = this.isControlled() ? current : defaultCurrent;
const _index = items.findIndex((u) => initCurrent === u.label);
this.setData({
currentKey: _index,
touchKeyIndex: _index,
touchKey: initCurrent,
});
},
didUpdate(_prop) {
const { current, items } = this.props;
if (_prop.current !== current) {
const _index = items.findIndex((u) => current === u.label);
this.setData({ currentKey: _index });
this.setData({
currentKey: _index,
});
if (!this.isControlled()) {
this.setData({
touchKeyIndex: _index,
touchKey: current,
});
}
}
},
methods: {
isControlled(valueKey = 'current') {
if ('controlled' in this.props) {
return this.props.controlled;
}
return valueKey in this.props;
},
// 初始化每个块的高度,用已计算滑动距离
initItemHeight() {
my.createSelectorQuery()
@ -110,7 +128,11 @@ Component({
newIndex = topRange.findIndex((h) => scrollTop + 1 < h);
}
if (currentKey !== newIndex - 1 && newIndex - 1 >= 0 && !moving) {
this.setData({ currentKey: newIndex - 1 });
this.setData({
currentKey: newIndex - 1,
touchKeyIndex: newIndex - 1,
touchKey: items[newIndex - 1].label,
});
this.onAlphabetClick(items[newIndex - 1], newIndex - 1);
}
},

View File

@ -1,49 +1,48 @@
import { IBaseProps } from '../_util/base';
interface ItemObj {
label: string,
disablePreview?: boolean
label: string;
disablePreview?: boolean;
}
export interface IndexBarProps extends IBaseProps {
/**
* @description
*/
activeClassName: string,
activeClassName: string;
/**
* @description
*/
defaultCurrent: string,
defaultCurrent: string;
/**
* @description
*/
current: string,
current: string;
/**
* @description
*/
vibrate: boolean,
vibrate: boolean;
/**
* @description
*/
items: ItemObj[],
items: ItemObj[];
/**
* @description
*/
size: number,
size: number;
/**
* @description
*/
onChange: (value: ItemObj, index: number) => void,
onChange: (value: ItemObj, index: number) => void;
}
export const IndexBarDefaultProps: Partial<IndexBarProps> = {
className: '',
activeClassName: '',
defaultCurrent: '',
current: '',
defaultCurrent: null,
vibrate: true,
items: [],
size: 16,
style: '',
onChange: (value, index) => {},
}
};

View File

@ -21,6 +21,10 @@ const config: IConfig = {
content:
'width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, viewport-fit=cover',
},
{
name: 'google-site-verification',
content: 'kzh4AXx63f3ZC1w2W_k4DWX4L2p_4hRcP5SsRmmMk8k',
},
],
headScripts: [
{

View File

@ -0,0 +1,9 @@
.base {
width: 100%;
height: 100vh;
}
.indexbar {
position: fixed;
right: 10px;
top: 20vh;
}

View File

@ -0,0 +1,17 @@
<view class="base">
<index-bar
className="indexbar"
items="{{items}}"
current="{{current}}"
onChange="onChange">
<view slot-scope="props">
<list header="{{props.value.label}}">
<list-item
a:for="{{nameMap[props.value.label]}}"
a:for-item="itemX">
{{itemX}}
</list-item>
</list>
</view>
</index-bar>
</view>

View File

@ -0,0 +1,47 @@
const nameMap = {
'A': ['Alice', 'Andy', 'Amanda'],
'B': ['Bob', 'Brian', 'Bella'],
'C': ['Cathy', 'Carl', 'Chris'],
'D': ['David', 'Diana', 'Derek'],
'E': ['Eva', 'Evan', 'Eddie'],
'F': ['Fiona', 'Frank', 'Fred'],
'G': ['George', 'Grace', 'Gavin'],
'H': ['Helen', 'Hank', 'Harry'],
'I': ['Ivy', 'Ian', 'Isaac'],
'J': ['Jack', 'Jill', 'James'],
'K': ['Karen', 'Kyle', 'Kurt'],
'L': ['Laura', 'Liam', 'Leo'],
'M': ['Megan', 'Mike', 'Mona'],
'N': ['Nina', 'Nick', 'Nancy'],
'O': ['Olivia', 'Oscar', 'Owen'],
'P': ['Paul', 'Pam', 'Pete'],
'Q': ['Quincy', 'Quinn', 'Queen'],
'R': ['Rachel', 'Ryan', 'Rita'],
'S': ['Sam', 'Sara', 'Steve'],
'T': ['Tom', 'Tina', 'Tim'],
'U': ['Uma', 'Ulysses', 'Ursula'],
'V': ['Violet', 'Victor', 'Vince'],
'W': ['Wendy', 'Will', 'Wanda'],
'X': ['Xander', 'Xenia', 'Xavier'],
'Y': ['Yara', 'Yuri', 'Yvonne'],
'Z': ['Zoe', 'Zach', 'Zane'],
};
Page({
data: {
nameMap,
items: [],
current: 'S',
},
onLoad() {
this.setData({
items: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('').map((u) => {
return { label: u, disablePreview: true };
}),
});
},
onChange(item, index) {
console.log(item, index);
this.setData({ current: item.label });
},
});

View File

@ -0,0 +1,11 @@
{
"defaultTitle": "IndexBar",
"pullRefresh": "NO",
"showProgress": "NO",
"allowsBounceVertical": "NO",
"usingComponents": {
"index-bar": "../../../src/IndexBar/index",
"list": "../../../src/List/index",
"list-item": "../../../src/List/ListItem/index"
}
}

View File

@ -2,7 +2,7 @@
<index-bar
className="indexbar"
items="{{items}}"
defaultCurrent="A"
defaultCurrent="H"
onChange="onChange">
<view slot-scope="props">
<list header="{{props.value.label}}">

View File

@ -93,6 +93,11 @@ export const componentList = [
nameZN: '索引',
path: '/pages/IndexBarScrollView/index',
},
{
name: 'IndexBarControl',
nameZN: '索引-控制',
path: '/pages/IndexBarControl/index',
},
/// #endif
{
name: 'Collapse',

View File

@ -24,7 +24,7 @@
</view>
<view
class="ant-indexbar-side-item-tip"
a:if="{{item.label === touchKey && !item.disablePreview}}">
a:if="{{item.label === touchKey && !item.disablePreview && moving}}">
{{item.label}}
<slot
value="{{item}}"

View File

@ -20,27 +20,35 @@ toc: 'content'
## 代码示例
### 基本使用
<code src='pages/IndexBar/index'></code>
### 结合列表使用
<code src='pages/IndexBarScrollView/index'></code>
### 控制选中状态
<code src='pages/IndexBarControl/index'></code>
## API
| 属性 | 说明 | 类型 | 默认值 |
| ---- | ---- | ---- | ------ |
| activeClassName | 索引激活时的样式 | string | - |
| className | 类名 | string | - |
| current | 索引值 | string | - |
| defaultCurrent | 默认索引 | string | - |
| labelPreview | 索引预览内容,接收 value 和 index | slot | - |
| items | 索引数组 | [Item](#item) | [] |
| style | 样式 | string | - |
| size | 索引的尺寸(宽高,单位 px) | number | 16 |
| vibrate | 索引改变时是否震动 | boolean | true |
| onChange | 索引改变时的回调 | (value: [Item](#item), index: number) => void |
| 属性 | 说明 | 类型 | 默认值 |
| --------------- | --------------------------------- | --------------------------------------------- | ------ |
| activeClassName | 索引激活时的样式 | string | - |
| className | 类名 | string | - |
| current | 索引值 | string | - |
| defaultCurrent | 默认索引 | string | - |
| labelPreview | 索引预览内容,接收 value 和 index | slot | - |
| items | 索引数组 | [Item](#item) | [] |
| style | 样式 | string | - |
| size | 索引的尺寸(宽高,单位 px) | number | 16 |
| vibrate | 索引改变时是否震动 | boolean | true |
| onChange | 索引改变时的回调 | (value: [Item](#item), index: number) => void |
#### Item
| 属性 | 说明 | 类型 | 默认值 |
| ---- | ---- | ---- | ------ |
| label | 索引标识 | string | - |
| disablePreview | 禁用索引触发时的预览效果 | boolean | - |
| 属性 | 说明 | 类型 | 默认值 |
| -------------- | ------------------------ | ------- | ------ |
| label | 索引标识 | string | - |
| disablePreview | 禁用索引触发时的预览效果 | boolean | - |

View File

@ -1,5 +1,4 @@
import { IndexBarDefaultProps } from './props';
import '../_util/assert-component2';
Component({
props: IndexBarDefaultProps,
@ -15,20 +14,39 @@ Component({
hasDefaultSlot: true,
},
didMount() {
const { defaultCurrent, items } = this.props;
const { defaultCurrent, current, items } = this.props;
this.initItemHeight();
this.initTopRange();
const _index = items.findIndex((u) => defaultCurrent === u.label);
this.setData({ currentKey: _index });
const initCurrent = this.isControlled() ? current : defaultCurrent;
const _index = items.findIndex((u) => initCurrent === u.label);
this.setData({
currentKey: _index,
touchKeyIndex: _index,
touchKey: initCurrent,
});
},
didUpdate(_prop) {
const { current, items } = this.props;
if (_prop.current !== current) {
const _index = items.findIndex((u) => current === u.label);
this.setData({ currentKey: _index });
this.setData({
currentKey: _index,
});
if (!this.isControlled()) {
this.setData({
touchKeyIndex: _index,
touchKey: current,
});
}
}
},
methods: {
isControlled(valueKey = 'current') {
if ('controlled' in this.props) {
return this.props.controlled;
}
return valueKey in this.props;
},
// 初始化每个块的高度,用已计算滑动距离
initItemHeight() {
my.createSelectorQuery()
@ -110,7 +128,11 @@ Component({
newIndex = topRange.findIndex((h) => scrollTop + 1 < h);
}
if (currentKey !== newIndex - 1 && newIndex - 1 >= 0 && !moving) {
this.setData({ currentKey: newIndex - 1 });
this.setData({
currentKey: newIndex - 1,
touchKeyIndex: newIndex - 1,
touchKey: items[newIndex - 1].label,
});
this.onAlphabetClick(items[newIndex - 1], newIndex - 1);
}
},

View File

@ -1,49 +1,48 @@
import { IBaseProps } from '../_util/base';
interface ItemObj {
label: string,
disablePreview?: boolean
label: string;
disablePreview?: boolean;
}
export interface IndexBarProps extends IBaseProps {
/**
* @description
*/
activeClassName: string,
activeClassName: string;
/**
* @description
*/
defaultCurrent: string,
defaultCurrent: string;
/**
* @description
*/
current: string,
current: string;
/**
* @description
*/
vibrate: boolean,
vibrate: boolean;
/**
* @description
*/
items: ItemObj[],
items: ItemObj[];
/**
* @description
*/
size: number,
size: number;
/**
* @description
*/
onChange: (value: ItemObj, index: number) => void,
onChange: (value: ItemObj, index: number) => void;
}
export const IndexBarDefaultProps: Partial<IndexBarProps> = {
className: '',
activeClassName: '',
defaultCurrent: '',
current: '',
defaultCurrent: null,
vibrate: true,
items: [],
size: 16,
style: '',
onChange: (value, index) => {},
}
};