-
+
> & {
handleClick: setSelectIndex,
}}
>
- {React.Children.map(children, (child, index) => {
- if (!React.isValidElement(child)) return null
- return React.cloneElement(child, { ...child.props, index })
- })}
+ {React.Children.map(children, (child, index) =>
+ React.isValidElement(child)
+ ? React.cloneElement(child, {
+ ...child.props,
+ index,
+ direction: itemDirection,
+ })
+ : null
+ )}
- {(fixed || safeArea) &&
}
+ {(fixed || safeArea) &&
}
)
}
diff --git a/src/packages/tabbaritem/tabbaritem.scss b/src/packages/tabbaritem/tabbaritem.scss
index fcdec7da14..ccb308ffa5 100644
--- a/src/packages/tabbaritem/tabbaritem.scss
+++ b/src/packages/tabbaritem/tabbaritem.scss
@@ -3,45 +3,48 @@
.nut-tabbar-item {
display: flex;
flex-direction: column;
- justify-content: center;
align-items: center;
flex: 1;
- text-align: center;
- text-decoration: none;
+ padding: 6px 0 2px;
color: $tabbar-inactive-color;
height: 100%;
- &-icon-box {
- padding: 0px;
- display: flex;
- flex-direction: column;
- align-items: center;
- position: relative;
+ .nut-icon {
+ width: 24px;
+ height: 24px;
+ font-size: 24px;
+ /* #ifdef harmony*/
+ color: $tabbar-inactive-color;
+ /* #endif*/
+ /* #ifndef harmony*/
+ color: inherit;
+ /* #endif*/
+ }
- .nut-icon {
- width: 22px;
- height: 22px;
- font-size: 22px;
- /* #ifdef harmony*/
- color: $tabbar-inactive-color;
- /* #endif*/
- /* #ifndef harmony*/
- color: inherit;
- /* #endif*/
- }
+ &-text {
+ display: block;
+ color: $color-text;
+ font-size: $tabbar-text-font-size;
+ line-height: $tabbar-text-font-size;
+ margin-top: $tabbar-text-margin-top;
+ }
- &-nav {
- display: block;
- color: $color-text;
- font-size: $tabbar-text-font-size;
- line-height: $tabbar-text-font-size;
- margin-top: $tabbar-text-margin-top;
+ .nut-image {
+ &-default {
+ width: 38px;
+ height: 38px;
+ border-radius: 38px;
}
+ }
+
+ &-large {
+ justify-content: center;
+ padding: 0;
- &-large {
+ .nut-tabbar-item-text {
font-size: $tabbar-text-large-font-size;
margin-top: 0;
- line-height: $tabbar-text-line-height;
+ line-height: $tabbar-text-large-font-size;
font-weight: $tabbar-text-large-font-weight;
}
}
@@ -49,7 +52,7 @@
&-active {
color: $tabbar-active-color;
- .nut-tabbar-item-icon-box,
+ .nut-tabbar-item-text,
.nut-icon {
/* #ifdef harmony*/
color: $tabbar-active-color;
diff --git a/src/packages/tabbaritem/tabbaritem.taro.tsx b/src/packages/tabbaritem/tabbaritem.taro.tsx
index 5c66c57d1c..a4ad41806d 100644
--- a/src/packages/tabbaritem/tabbaritem.taro.tsx
+++ b/src/packages/tabbaritem/tabbaritem.taro.tsx
@@ -1,10 +1,9 @@
-import React, { FunctionComponent, useContext } from 'react'
+import React, { FunctionComponent, useContext, ReactNode } from 'react'
import classNames from 'classnames'
import { View } from '@tarojs/components'
import { ComponentDefaults } from '@/utils/typings'
import Badge from '@/packages/badge/index.taro'
import TabbarContext from '@/packages/tabbar/context'
-import addColorForHarmony from '@/utils/add-color-for-harmony'
import { TaroTabbarItemProps } from '@/types'
const defaultProps = {
@@ -16,6 +15,7 @@ const defaultProps = {
max: 99,
top: '0',
right: '0',
+ direction: 'vertical',
} as TaroTabbarItemProps
export const TabbarItem: FunctionComponent
> = (
@@ -34,6 +34,8 @@ export const TabbarItem: FunctionComponent> = (
right,
// @ts-ignore
index,
+ direction,
+ onActiveClick,
...rest
} = {
...defaultProps,
@@ -45,16 +47,18 @@ export const TabbarItem: FunctionComponent> = (
classPrefix,
{
[`${classPrefix}-active`]: active,
+ [`${classPrefix}-large`]: !icon || !title,
},
className
)
- const boxPrefix = `${classPrefix}-icon-box`
- const titleClass = classNames(boxPrefix, `${boxPrefix}-nav`, {
- [`${boxPrefix}-large`]: !icon,
- })
+ const renderNodeWithActive = (
+ node: ReactNode | ((active: boolean) => ReactNode)
+ ) => {
+ return node && typeof node === 'function' ? node(active) : node
+ }
const badgeProps = {
- value,
+ value: renderNodeWithActive(value),
dot,
max,
top,
@@ -66,17 +70,57 @@ export const TabbarItem: FunctionComponent> = (
return (
title && (
- {title}
+ {renderNodeWithActive(title)}
)
)
}
+ const renderTitle = () => {
+ return (
+
+ {renderTitleText()}
+
+ )
+ }
+
+ const renderIcon = () => {
+ const distIcon = renderNodeWithActive(icon)
+ // 鸿蒙差异处理,需要手动给icon一个color
+ return React.isValidElement(distIcon)
+ ? React.cloneElement(distIcon, {
+ ...distIcon.props,
+ color: active ? ctx?.activeColor : ctx?.inactiveColor,
+ })
+ : null
+ }
+
+ const renderIconAndTitle = () => {
+ return (
+ <>
+
+ {renderIcon()}
+
+ {renderTitleText()}
+ >
+ )
+ }
+
+ const renderDualItem = () => {
+ return dot ? null : (
+ <>
+ {renderIcon()}
+ {renderTitleText()}
+
+ >
+ )
+ }
+
return (
> = (
color: active ? ctx?.activeColor : ctx?.inactiveColor,
...style,
}}
- onClick={() => ctx?.handleClick(index)}
+ onClick={() => (active ? onActiveClick?.() : ctx?.handleClick(index))}
{...rest}
>
- {icon ? (
+ {direction === 'horizontal' && !dot ? (
+ renderDualItem()
+ ) : (
<>
-
-
- {addColorForHarmony(
- icon,
- active ? ctx?.activeColor : ctx?.inactiveColor
- )}
-
-
- {renderTitleText()}
+ {icon && renderIconAndTitle()}
+ {!icon && renderTitle()}
>
- ) : (
- {renderTitleText()}
)}
)
diff --git a/src/packages/tabbaritem/tabbaritem.tsx b/src/packages/tabbaritem/tabbaritem.tsx
index bba38b14f7..c68bffce85 100644
--- a/src/packages/tabbaritem/tabbaritem.tsx
+++ b/src/packages/tabbaritem/tabbaritem.tsx
@@ -1,4 +1,4 @@
-import React, { FunctionComponent, useContext } from 'react'
+import React, { FunctionComponent, useContext, ReactNode } from 'react'
import classNames from 'classnames'
import { ComponentDefaults } from '@/utils/typings'
import Badge from '@/packages/badge/index'
@@ -14,6 +14,7 @@ const defaultProps = {
max: 99,
top: '0',
right: '0',
+ direction: 'vertical',
} as WebTabbarItemProps
export const TabbarItem: FunctionComponent> = (
@@ -32,6 +33,8 @@ export const TabbarItem: FunctionComponent> = (
right,
// @ts-ignore
index,
+ direction,
+ onActiveClick,
...rest
} = {
...defaultProps,
@@ -43,16 +46,19 @@ export const TabbarItem: FunctionComponent> = (
classPrefix,
{
[`${classPrefix}-active`]: active,
+ [`${classPrefix}-large`]: !icon || !title,
},
className
)
- const boxPrefix = `${classPrefix}-icon-box`
- const titleClass = classNames(boxPrefix, `${boxPrefix}-nav`, {
- [`${boxPrefix}-large`]: !icon,
- })
+
+ const renderNodeWithActive = (
+ node: ReactNode | ((active: boolean) => ReactNode)
+ ) => {
+ return node && typeof node === 'function' ? node(active) : node
+ }
const badgeProps = {
- value,
+ value: renderNodeWithActive(value),
dot,
max,
top,
@@ -61,7 +67,46 @@ export const TabbarItem: FunctionComponent> = (
}
const renderTitleText = () => {
- return title && {title}
+ return (
+ title && (
+
+ {renderNodeWithActive(title)}
+
+ )
+ )
+ }
+
+ const renderTitle = () => {
+ return (
+
+ {renderTitleText()}
+
+ )
+ }
+
+ const renderIcon = () => {
+ return renderNodeWithActive(icon)
+ }
+
+ const renderIconAndTitle = () => {
+ return (
+ <>
+
+ {renderIcon()}
+
+ {renderTitleText()}
+ >
+ )
+ }
+
+ const renderDualItem = () => {
+ return dot ? null : (
+ <>
+ {renderIcon()}
+ {renderTitleText()}
+
+ >
+ )
}
return (
@@ -71,18 +116,16 @@ export const TabbarItem: FunctionComponent> = (
color: active ? ctx?.activeColor : ctx?.inactiveColor,
...style,
}}
- onClick={() => ctx?.handleClick(index)}
+ onClick={() => (active ? onActiveClick?.() : ctx?.handleClick(index))}
{...rest}
>
- {icon ? (
+ {direction === 'horizontal' && !dot ? (
+ renderDualItem()
+ ) : (
<>
-
- {icon}
-
- {renderTitleText()}
+ {icon && renderIconAndTitle()}
+ {!icon && renderTitle()}
>
- ) : (
- {renderTitleText()}
)}
)
diff --git a/src/sites/sites-react/doc/docs/react/migrate-from-v2.md b/src/sites/sites-react/doc/docs/react/migrate-from-v2.md
index a9cdf02f2f..6ce2eca91c 100644
--- a/src/sites/sites-react/doc/docs/react/migrate-from-v2.md
+++ b/src/sites/sites-react/doc/docs/react/migrate-from-v2.md
@@ -137,8 +137,13 @@ plugins: [
- 注意:** 该组件不符合移动端规范,已被废弃。请使用 SideBar **
-[//]: # '#### Tabbar'
-[//]: # '#### TabbarItem'
+#### Tabbar
+
+#### TabbarItem
+
+- 为 `icon`、`title` 和 `value` 增加新的类型,支持 `function` 根据当前是否 `active` 状态展示不同的 icon/title/value。
+- 增加 `onActiveClick` 事件,用于处理当元素处于焦点时,再次点击时可增加自定义事件。
+
[//]: # '#### Tabs'
[//]: # '#### Tabs.Tabpane'
diff --git a/src/sites/sites-react/doc/docs/taro/migrate-from-v2.md b/src/sites/sites-react/doc/docs/taro/migrate-from-v2.md
index 4d673d5814..7be61352ac 100644
--- a/src/sites/sites-react/doc/docs/taro/migrate-from-v2.md
+++ b/src/sites/sites-react/doc/docs/taro/migrate-from-v2.md
@@ -137,8 +137,13 @@ plugins: [
- 注意:** 该组件不符合移动端规范,已被废弃。请使用 SideBar **
-[//]: # '#### Tabbar'
-[//]: # '#### TabbarItem'
+#### Tabbar
+
+#### TabbarItem
+
+- 为 `icon`、`title` 和 `value` 增加新的类型,支持 `function` 根据当前是否 `active` 状态展示不同的 icon/title/value。
+- 增加 `onActiveClick` 事件,用于处理当元素处于焦点时,再次点击时可增加自定义事件。
+
[//]: # '#### Tabs'
[//]: # '#### Tabs.Tabpane'
@@ -162,11 +167,7 @@ plugins: [
- 移除 `async`, 可通过 `beforeChange` 替代
- 增加 `beforeChange`, 处理异步调用
- [//]: # '#### NumberKeyboard'
- [//]: # '#### Picker'
- [//]: # '#### Radio'
- [//]: # '### Radio.Group'
- [//]: # '#### Range'
+
[//]: # '#### NumberKeyboard'
[//]: # '#### Picker'
[//]: # '#### Radio'
diff --git a/src/styles/variables.scss b/src/styles/variables.scss
index 3276b5699d..79ad6da470 100644
--- a/src/styles/variables.scss
+++ b/src/styles/variables.scss
@@ -1135,21 +1135,18 @@ $rate-font-color: var(--nutui-rate-font-color, $color-primary-icon) !default;
$rate-font-size: var(--nutui-rate-font-size, 12px) !default;
// tabbar(✅)
-$tabbar-height: var(--nutui-tabbar-height, 50px) !default;
+$tabbar-height: var(--nutui-tabbar-height, 46px) !default;
$tabbar-active-color: var(--nutui-tabbar-active-color, $color-primary) !default;
$tabbar-inactive-color: var(
--nutui-tabbar-inactive-color,
$color-title
) !default;
-$tabbar-border-top: var(--nutui-tabbar-border-top, 1px solid #eee) !default;
-$tabbar-border-bottom: var(
- --nutui-tabbar-border-bottom,
- 1px solid #eee
-) !default;
+$tabbar-border-top: var(--nutui-tabbar-border-top, 0) !default;
+$tabbar-border-bottom: var(--nutui-tabbar-border-bottom, 0) !default;
$tabbar-box-shadow: var(--nutui-tabbar-box-shadow, none) !default;
$tabbar-text-font-size: var(
--nutui-tabbar-text-font-size,
- $font-size-xs
+ $font-size-xxs
) !default;
$tabbar-text-large-font-size: var(
--nutui-tabbar-text-large-font-size,
@@ -1160,8 +1157,7 @@ $tabbar-text-large-font-weight: var(
$font-weight
) !default;
$tabbar-text-line-height: var(--nutui-tabbar-text-line-height, 20px) !default;
-$tabbar-height: var(--nutui-tabbar-height, 50px) !default;
-$tabbar-text-margin-top: var(--nutui-tabbar-text-margin-top, 3px) !default;
+$tabbar-text-margin-top: var(--nutui-tabbar-text-margin-top, 4px) !default;
//pulltorefresh
$pulltorefresh-icon-width: var(--nutui-pulltorefresh-icon-width, 36px) !default;
diff --git a/src/types/spec/tabbar/base.ts b/src/types/spec/tabbar/base.ts
index 8eaca5fbb8..7f26ab20e2 100644
--- a/src/types/spec/tabbar/base.ts
+++ b/src/types/spec/tabbar/base.ts
@@ -1,5 +1,5 @@
import { ReactNode } from 'react'
-import { BaseProps } from '../../base/props'
+import { BaseProps, Direction } from '@/types'
export interface BaseTabbar extends BaseProps {
defaultValue: number
@@ -7,16 +7,19 @@ export interface BaseTabbar extends BaseProps {
fixed: boolean
inactiveColor: string
activeColor: string
+ direction: Direction
safeArea: boolean
onSwitch: (value: number) => void
}
export interface BaseTabbarItem extends BaseProps {
- title: ReactNode
- icon: ReactNode
- value: ReactNode
+ title: ReactNode | ((active: boolean) => ReactNode)
+ icon: ReactNode | ((active: boolean) => ReactNode)
+ value: ReactNode | ((active: boolean) => ReactNode)
dot: boolean
max: number
top: string
right: string
+ direction: Direction
+ onActiveClick: () => void
}