diff --git a/migrate-from-v2.md b/migrate-from-v2.md
index cb1ec0b79..1b596afe6 100644
--- a/migrate-from-v2.md
+++ b/migrate-from-v2.md
@@ -291,6 +291,16 @@ plugins: [
- `offset` 重命名为 `indent`
+#### SideBar
+
+- 新增SideBar组件
+- 支持属性value,用于当前激活的`item`的key
+- 支持属性defaultValue, 表示未设置value时,`item`的key的默认值
+- 支持属性contentDuration, 用于内容滚动动画时长
+- 支持属性sidebarDuration, 用于侧栏滚动动画时长
+- 支持属性onClick, 点击标签时触发
+- 支持属性onChange, 当前激活的标签改变时触发
+
#### Tabbar
- `unactiveColor` 重命名为 `inactiveColor`
@@ -597,7 +607,7 @@ plugins: [
- 移除 `isAsync`,通过 `checked`实现
- 移除 `activeColor` ,通过css变量`--nutui-switch-open-background-color`实现
- 移除 `inactiveColor`,通过css变量`--nutui-switch-close-background-color`实现
-- `activeText 属性类型更改为 `ReactNode`
+- `activeText` 属性类型更改为`ReactNode`
- `inactiveText` 属性类型更改为 `ReactNode`
#### Toast
@@ -780,7 +790,7 @@ plugins: [
- 移除 `pageContent`,通过 indicator 实现
- `autoplay` 重命名为 `autoplay`
- `initPage` 重命名为 `defaultValue`
- - `paginationVisible` 重命名为 `indicator`,类型改为` ReactNode`
+ - `paginationVisible` 重命名为 `indicator`,类型改为`ReactNode`
- `isPreventDefault` 重命名为 `preventDefault`
- `isStopPropagation` 重命名为 `stopPropagation`
- `isCenter` 重命名为 `center`
diff --git a/src/config.json b/src/config.json
index 30e85c3ff..618306656 100644
--- a/src/config.json
+++ b/src/config.json
@@ -345,6 +345,30 @@
"taro": true,
"author": "hx"
},
+ {
+ "version": "3.0.0",
+ "name": "SideBar",
+ "type": "component",
+ "cName": "侧边栏导航",
+ "desc": "用于侧边内容选择和切换",
+ "sort": 10,
+ "show": true,
+ "taro": true,
+ "author": "Alex.hxy",
+ "v15": true
+ },
+ {
+ "version": "3.0.0",
+ "name": "SideBarItem",
+ "type": "component",
+ "cName": "侧边栏导航子组件",
+ "desc": "用于侧边内容选择和切换",
+ "sort": 10,
+ "show": false,
+ "taro": true,
+ "author": "Alex.hxy",
+ "v15": true
+ },
{
"version": "2.0.0",
"name": "SideNavBarItem",
diff --git a/src/packages/sidebar/_test_/sidebar.spec.tsx b/src/packages/sidebar/_test_/sidebar.spec.tsx
new file mode 100644
index 000000000..25462fd1f
--- /dev/null
+++ b/src/packages/sidebar/_test_/sidebar.spec.tsx
@@ -0,0 +1,70 @@
+import * as React from 'react'
+import { fireEvent, render } from '@testing-library/react'
+import '@testing-library/jest-dom'
+import { SideBar } from '../sidebar'
+
+const list = Array.from(new Array(3).keys())
+
+test('should render defaultValue correctly', async () => {
+ const { container } = render(
+
+ {list.map((item) => (
+
+ Content {item + 1}
+
+ ))}
+
+ )
+ const item = container.querySelectorAll('.nut-sidebar-titles-item')[0]
+ expect(item).toHaveClass('nut-sidebar-titles-item-active')
+})
+
+test('should choose and scroll to the right option', async () => {
+ const onChange = vi.fn()
+ const { container } = render(
+
+ {list.map((item) => (
+
+ Content {item + 1}
+
+ ))}
+
+ )
+ const items = container.querySelectorAll('.nut-sidebar-titles-item')
+ fireEvent.click(items[1])
+ expect(onChange).toHaveBeenCalledWith(1)
+})
+test('disabled option', async () => {
+ const onChange = vi.fn()
+ const { container } = render(
+
+ {list.map((item) => (
+
+ Content {item + 1}
+
+ ))}
+
+ )
+ const items = container.querySelectorAll('.nut-sidebar-titles-item')
+ fireEvent.click(items[1])
+ expect(onChange).not.toHaveBeenCalled()
+})
+test('matchByValue', async () => {
+ const list1 = [
+ { value: 'a', title: 'Opt a Opt a Opt a Opt a' },
+ { value: 'b', title: 'Opt b' },
+ { value: 'c', title: 'Opt c' },
+ ]
+ const onChange = vi.fn()
+ const { container } = render(
+
+ {list1.map((item) => (
+
+ Content {item.value}
+
+ ))}
+
+ )
+ const items = container.querySelectorAll('.nut-sidebar-titles-item')
+ expect(items[1]).toHaveClass('nut-sidebar-titles-item-active')
+})
diff --git a/src/packages/sidebar/demo.taro.tsx b/src/packages/sidebar/demo.taro.tsx
new file mode 100644
index 000000000..9b50b5b2e
--- /dev/null
+++ b/src/packages/sidebar/demo.taro.tsx
@@ -0,0 +1,56 @@
+import React from 'react'
+import Taro from '@tarojs/taro'
+import { ScrollView, View } from '@tarojs/components'
+import { useTranslate } from '@/sites/assets/locale/taro'
+import Header from '@/sites/components/header'
+import Demo1 from './demos/taro/demo1'
+import Demo2 from './demos/taro/demo2'
+import Demo3 from './demos/taro/demo3'
+import Demo4 from './demos/taro/demo4'
+import Demo5 from './demos/taro/demo5'
+import Demo6 from './demos/taro/demo6'
+
+const TabsDemo = () => {
+ const [translated] = useTranslate({
+ 'zh-CN': {
+ basic: '基础用法',
+ disabled: '禁用选项',
+ matchByValue: '根据value匹配',
+ multiTitle: '多个标题',
+ setDuration: '设置滚动动画时长',
+ padding: '内容区域留白边距',
+ },
+ 'en-US': {
+ basic: 'Basic Usage',
+ disabled: 'Disabled',
+ matchByValue: 'Match By Value',
+ multiTitle: 'Multiple Titles',
+ setDuration: 'Set Scroll Animation Duration',
+ padding: 'Set Content Padding',
+ },
+ })
+
+ return (
+ <>
+
+
+ {translated.basic}
+
+ {translated.disabled}
+
+ {translated.matchByValue}
+
+ {translated.multiTitle}
+
+ {translated.setDuration}
+
+ {translated.padding}
+
+
+ >
+ )
+}
+
+export default TabsDemo
diff --git a/src/packages/sidebar/demo.tsx b/src/packages/sidebar/demo.tsx
new file mode 100644
index 000000000..1c9d9d52a
--- /dev/null
+++ b/src/packages/sidebar/demo.tsx
@@ -0,0 +1,50 @@
+import React from 'react'
+import { useTranslate } from '@/sites/assets/locale'
+import Demo1 from './demos/h5/demo1'
+import Demo2 from './demos/h5/demo2'
+import Demo3 from './demos/h5/demo3'
+import Demo4 from './demos/h5/demo4'
+import Demo5 from './demos/h5/demo5'
+import Demo6 from './demos/h5/demo6'
+
+const SideNavBarDemo = () => {
+ const [translated] = useTranslate({
+ 'zh-CN': {
+ basic: '基础用法',
+ disabled: '禁用选项',
+ matchByValue: '根据value匹配',
+ multiTitle: '多个标题',
+ setDuration: '设置滚动动画时长',
+ padding: '内容区域留白边距',
+ },
+ 'en-US': {
+ basic: 'Basic Usage',
+ disabled: 'Disabled',
+ matchByValue: 'Match By Value',
+ multiTitle: 'Multiple Titles',
+ setDuration: 'Set Scroll Animation Duration',
+ padding: 'Set Content Padding',
+ },
+ })
+
+ return (
+ <>
+
+
{translated.basic}
+
+ {translated.disabled}
+
+ {translated.matchByValue}
+
+ {translated.multiTitle}
+
+ {translated.setDuration}
+
+ {translated.padding}
+
+
+ >
+ )
+}
+
+export default SideNavBarDemo
diff --git a/src/packages/sidebar/demos/h5/demo1.tsx b/src/packages/sidebar/demos/h5/demo1.tsx
new file mode 100644
index 000000000..8eede1cef
--- /dev/null
+++ b/src/packages/sidebar/demos/h5/demo1.tsx
@@ -0,0 +1,25 @@
+import React, { useState } from 'react'
+import { SideBar } from '@nutui/nutui-react'
+
+const Demo1 = () => {
+ const [value, setValue] = useState('0')
+ const list = Array.from(new Array(3).keys())
+ return (
+ <>
+ {
+ setValue(value)
+ }}
+ >
+ {list.map((item) => (
+
+ Content {item + 1}
+
+ ))}
+
+ >
+ )
+}
+export default Demo1
diff --git a/src/packages/sidebar/demos/h5/demo2.tsx b/src/packages/sidebar/demos/h5/demo2.tsx
new file mode 100644
index 000000000..faef94081
--- /dev/null
+++ b/src/packages/sidebar/demos/h5/demo2.tsx
@@ -0,0 +1,22 @@
+import React, { useState } from 'react'
+import { SideBar } from '@nutui/nutui-react'
+
+const Demo2 = () => {
+ const [value, setValue] = useState('0')
+ return (
+ <>
+ {
+ setValue(value)
+ }}
+ >
+ Content 1
+ Content 2
+
+
+ >
+ )
+}
+export default Demo2
diff --git a/src/packages/sidebar/demos/h5/demo3.tsx b/src/packages/sidebar/demos/h5/demo3.tsx
new file mode 100644
index 000000000..1fe093760
--- /dev/null
+++ b/src/packages/sidebar/demos/h5/demo3.tsx
@@ -0,0 +1,28 @@
+import React, { useState } from 'react'
+import { SideBar } from '@nutui/nutui-react'
+
+const Demo3 = () => {
+ const [value, setValue] = useState('b')
+ return (
+ <>
+ {
+ setValue(value)
+ }}
+ >
+
+ Content 1
+
+
+ Content 2
+
+
+ Content 3
+
+
+ >
+ )
+}
+export default Demo3
diff --git a/src/packages/sidebar/demos/h5/demo4.tsx b/src/packages/sidebar/demos/h5/demo4.tsx
new file mode 100644
index 000000000..1753d010d
--- /dev/null
+++ b/src/packages/sidebar/demos/h5/demo4.tsx
@@ -0,0 +1,25 @@
+import React, { useState } from 'react'
+import { SideBar } from '@nutui/nutui-react'
+
+const Demo4 = () => {
+ const [value, setValue] = useState('0')
+ const list = Array.from(new Array(20).keys())
+ return (
+ <>
+ {
+ setValue(value)
+ }}
+ >
+ {list.map((item) => (
+
+ Content {item + 1}
+
+ ))}
+
+ >
+ )
+}
+export default Demo4
diff --git a/src/packages/sidebar/demos/h5/demo5.tsx b/src/packages/sidebar/demos/h5/demo5.tsx
new file mode 100644
index 000000000..86fb9502b
--- /dev/null
+++ b/src/packages/sidebar/demos/h5/demo5.tsx
@@ -0,0 +1,27 @@
+import React, { useState } from 'react'
+import { SideBar } from '@nutui/nutui-react'
+
+const Demo5 = () => {
+ const [value, setValue] = useState('0')
+ const list = Array.from(new Array(20).keys())
+ return (
+ <>
+ {
+ setValue(value)
+ }}
+ >
+ {list.map((item) => (
+
+ Content {item + 1}
+
+ ))}
+
+ >
+ )
+}
+export default Demo5
diff --git a/src/packages/sidebar/demos/h5/demo6.tsx b/src/packages/sidebar/demos/h5/demo6.tsx
new file mode 100644
index 000000000..3ce3a8b27
--- /dev/null
+++ b/src/packages/sidebar/demos/h5/demo6.tsx
@@ -0,0 +1,27 @@
+import React, { useState } from 'react'
+import { ConfigProvider, SideBar } from '@nutui/nutui-react'
+
+const Demo6 = () => {
+ const [value, setValue] = useState('0')
+ const list = Array.from(new Array(3).keys())
+ return (
+
+ {
+ setValue(value)
+ }}
+ >
+ {list.map((item) => (
+
+ Content {item + 1}
+
+ ))}
+
+
+ )
+}
+export default Demo6
diff --git a/src/packages/sidebar/demos/taro/demo1.tsx b/src/packages/sidebar/demos/taro/demo1.tsx
new file mode 100644
index 000000000..50a63962c
--- /dev/null
+++ b/src/packages/sidebar/demos/taro/demo1.tsx
@@ -0,0 +1,25 @@
+import React, { useState } from 'react'
+import { SideBar } from '@nutui/nutui-react-taro'
+
+const Demo1 = () => {
+ const [value, setValue] = useState('0')
+ const list = Array.from(new Array(3).keys())
+ return (
+ <>
+ {
+ setValue(value)
+ }}
+ >
+ {list.map((item) => (
+
+ Content {item + 1}
+
+ ))}
+
+ >
+ )
+}
+export default Demo1
diff --git a/src/packages/sidebar/demos/taro/demo2.tsx b/src/packages/sidebar/demos/taro/demo2.tsx
new file mode 100644
index 000000000..a741d9eb1
--- /dev/null
+++ b/src/packages/sidebar/demos/taro/demo2.tsx
@@ -0,0 +1,22 @@
+import React, { useState } from 'react'
+import { SideBar } from '@nutui/nutui-react-taro'
+
+const Demo2 = () => {
+ const [value, setValue] = useState('0')
+ return (
+ <>
+ {
+ setValue(value)
+ }}
+ >
+ Content 1
+ Content 2
+
+
+ >
+ )
+}
+export default Demo2
diff --git a/src/packages/sidebar/demos/taro/demo3.tsx b/src/packages/sidebar/demos/taro/demo3.tsx
new file mode 100644
index 000000000..6dab28f3e
--- /dev/null
+++ b/src/packages/sidebar/demos/taro/demo3.tsx
@@ -0,0 +1,28 @@
+import React, { useState } from 'react'
+import { SideBar } from '@nutui/nutui-react-taro'
+
+const Demo3 = () => {
+ const [value, setValue] = useState('b')
+ return (
+ <>
+ {
+ setValue(value)
+ }}
+ >
+
+ Content 1
+
+
+ Content 2
+
+
+ Content 3
+
+
+ >
+ )
+}
+export default Demo3
diff --git a/src/packages/sidebar/demos/taro/demo4.tsx b/src/packages/sidebar/demos/taro/demo4.tsx
new file mode 100644
index 000000000..f08ea9ef3
--- /dev/null
+++ b/src/packages/sidebar/demos/taro/demo4.tsx
@@ -0,0 +1,25 @@
+import React, { useState } from 'react'
+import { SideBar } from '@nutui/nutui-react-taro'
+
+const Demo4 = () => {
+ const [value, setValue] = useState('0')
+ const list = Array.from(new Array(20).keys())
+ return (
+ <>
+ {
+ setValue(value)
+ }}
+ >
+ {list.map((item) => (
+
+ Content {item + 1}
+
+ ))}
+
+ >
+ )
+}
+export default Demo4
diff --git a/src/packages/sidebar/demos/taro/demo5.tsx b/src/packages/sidebar/demos/taro/demo5.tsx
new file mode 100644
index 000000000..515543da1
--- /dev/null
+++ b/src/packages/sidebar/demos/taro/demo5.tsx
@@ -0,0 +1,27 @@
+import React, { useState } from 'react'
+import { SideBar } from '@nutui/nutui-react-taro'
+
+const Demo5 = () => {
+ const [value, setValue] = useState('0')
+ const list = Array.from(new Array(20).keys())
+ return (
+ <>
+ {
+ setValue(value)
+ }}
+ >
+ {list.map((item) => (
+
+ Content {item + 1}
+
+ ))}
+
+ >
+ )
+}
+export default Demo5
diff --git a/src/packages/sidebar/demos/taro/demo6.tsx b/src/packages/sidebar/demos/taro/demo6.tsx
new file mode 100644
index 000000000..1062c6ca7
--- /dev/null
+++ b/src/packages/sidebar/demos/taro/demo6.tsx
@@ -0,0 +1,27 @@
+import React, { useState } from 'react'
+import { ConfigProvider, SideBar } from '@nutui/nutui-react-taro'
+
+const Demo6 = () => {
+ const [value, setValue] = useState('0')
+ const list = Array.from(new Array(3).keys())
+ return (
+
+ {
+ setValue(value)
+ }}
+ >
+ {list.map((item) => (
+
+ Content {item + 1}
+
+ ))}
+
+
+ )
+}
+export default Demo6
diff --git a/src/packages/sidebar/doc.en-US.md b/src/packages/sidebar/doc.en-US.md
new file mode 100644
index 000000000..a3fc90f20
--- /dev/null
+++ b/src/packages/sidebar/doc.en-US.md
@@ -0,0 +1,94 @@
+# SideBar component
+
+Used for side content selection and switching
+
+## Introduction
+
+```tsx
+import { SideBar } from '@nutui/nutui-react'
+```
+
+## Sample code
+
+### Basic usage
+
+:::demo
+
+
+
+:::
+
+### Disable option
+
+:::demo
+
+
+
+:::
+
+### Match based on value
+
+:::demo
+
+
+
+:::
+
+### Multiple titles
+
+:::demo
+
+
+
+:::
+
+### Set the scroll animation duration
+
+:::demo
+
+
+
+:::
+
+## SideBar
+
+### Props
+
+| Property | Description | Type | Default |
+| --- | --- | --- | --- |
+| value | The key of the currently activated `item` | `string \| number` | `-` |
+| defaultValue | When value is not set, the default value of the key of `item` | `string \| number` | `-` |
+| contentDuration | content scroll animation duration | `number` | `0` |
+| sidebarDuration | Sidebar scroll animation duration | `number` | `0` |
+| onClick | Triggered when the label is clicked | `(index: string \| number) => void` | `-` |
+| onChange | Triggered when the currently active label changes | `(index: string \| number) => void` | `-` |
+
+## SideBar.Item
+
+### Props
+
+| Property | Description | Type | Default |
+| --- | --- | --- | --- |
+| title | title | `string` | `-` |
+| value | tag Key, matched identifier, defaults to index value | `string` \| `number` | `-` |
+| disabled | Whether to disable the label | `boolean` | `false` |
+
+## Theming
+
+### CSS Variables
+
+The component provides the following CSS variables, which can be used to customize styles. Please refer to [ConfigProvider component](#/en-US/component/configprovider).
+
+| Name | Description | Default |
+| --- | --- | --- |
+| \--nutui-sidebar-background-color | Sidebar navigation background color | `$color-background` |
+| \--nutui-sidebar-border-radius | Rounded corners of the sidebar | `0` |
+| \--nutui-sidebar-width | Sidebar width | `104px` |
+| \--nutui-sidebar-max-width | Sidebar max width | `128px` |
+| \--nutui-sidebar-title-height | Sidebar title height | `52px` |
+| \--nutui-sidebar-inactive-font-size | Font size in normal state | `$font-size-base` |
+| \--nutui-sidebar-active-font-size | Font size in active state | `$font-size-l` |
+| \--nutui-sidebar-active-font-weight | Font weight in active state | `$font-weight-bold` |
+| \--nutui-sidebar-active-color | Font color in active state | `$color-primary` |
+| \--nutui-sidebar-item-background | The background color of the content area | `$white` |
+| \--nutui-sidebar-item-padding | Padding of the content area | `24px 20px` |
diff --git a/src/packages/sidebar/doc.md b/src/packages/sidebar/doc.md
new file mode 100644
index 000000000..6e720a5e8
--- /dev/null
+++ b/src/packages/sidebar/doc.md
@@ -0,0 +1,94 @@
+# SideBar组件
+
+用于侧边内容选择和切换
+
+## 引入
+
+```tsx
+import { SideBar } from '@nutui/nutui-react'
+```
+
+## 示例代码
+
+### 基础用法
+
+:::demo
+
+
+
+:::
+
+### 禁用选项
+
+:::demo
+
+
+
+:::
+
+### 根据value匹配
+
+:::demo
+
+
+
+:::
+
+### 多个标题
+
+:::demo
+
+
+
+:::
+
+### 设置滚动动画时长
+
+:::demo
+
+
+
+:::
+
+## SideBar
+
+### Props
+
+| 属性 | 说明 | 类型 | 默认值 |
+| --- | --- | --- | --- |
+| value | 当前激活的`item`的key | `string \| number` | `-` |
+| defaultValue | 未设置value时,`item`的key的默认值 | `string \| number` | `-` |
+| contentDuration | 内容滚动动画时长 | `number` | `0` |
+| sidebarDuration | 侧栏滚动动画时长 | `number` | `0` |
+| onClick | 点击标签时触发 | `(index: string \| number) => void` | `-` |
+| onChange | 当前激活的标签改变时触发 | `(index: string \| number) => void` | `-` |
+
+## SideBar.Item
+
+### Props
+
+| 属性 | 说明 | 类型 | 默认值 |
+| --- | --- | --- | --- |
+| title | 标题 | `string` | `-` |
+| value | 标签 Key , 匹配的标识符, 默认为索引值 | `string` \| `number` | `-` |
+| disabled | 是否禁用标签 | `boolean` | `false` |
+
+## 主题定制
+
+### 样式变量
+
+组件提供了下列 CSS 变量,可用于自定义样式,使用方法请参考 [ConfigProvider 组件](#/zh-CN/component/configprovider)。
+
+| 名称 | 说明 | 默认值 |
+| --- | --- | --- |
+| \--nutui-sidebar-background-color | 侧边栏导航背景色 | `$color-background` |
+| \--nutui-sidebar-border-radius | 侧边栏的圆角 | `0` |
+| \--nutui-sidebar-width | 侧边栏宽度 | `104px` |
+| \--nutui-sidebar-max-width | 侧边栏最大宽度 | `128px` |
+| \--nutui-sidebar-title-height | 侧边栏标题高度 | `52px` |
+| \--nutui-sidebar-inactive-font-size | 普通状态下的字体大小 | `$font-size-base` |
+| \--nutui-sidebar-active-font-size | 激活状态下的字体大小 | `$font-size-l` |
+| \--nutui-sidebar-active-font-weight | 激活状态下的字重 | `$font-weight-bold` |
+| \--nutui-sidebar-active-color | 激活状态下的字体颜色 | `$color-primary` |
+| \--nutui-sidebar-item-background | 内容区域的背景色 | `$white` |
+| \--nutui-sidebar-item-padding | 内容区域的内边距 | `24px 20px` |
diff --git a/src/packages/sidebar/doc.taro.md b/src/packages/sidebar/doc.taro.md
new file mode 100644
index 000000000..b51fbdf7b
--- /dev/null
+++ b/src/packages/sidebar/doc.taro.md
@@ -0,0 +1,94 @@
+# SideBar组件
+
+用于侧边内容选择和切换
+
+## 引入
+
+```tsx
+import { SideBar } from '@nutui/nutui-react-taro'
+```
+
+## 示例代码
+
+### 基础用法
+
+:::demo
+
+
+
+:::
+
+### 禁用选项
+
+:::demo
+
+
+
+:::
+
+### 根据value匹配
+
+:::demo
+
+
+
+:::
+
+### 多个标题
+
+:::demo
+
+
+
+:::
+
+### 设置滚动动画时长
+
+:::demo
+
+
+
+:::
+
+## SideBar
+
+### Props
+
+| 属性 | 说明 | 类型 | 默认值 |
+| --- | --- | --- | --- |
+| value | 当前激活的`item`的key | `string \| number` | `-` |
+| defaultValue | 未设置value时,`item`的key的默认值 | `string \| number` | `-` |
+| contentDuration | 内容滚动动画时长 | `number` | `0` |
+| sidebarDuration | 侧栏滚动动画时长 | `number` | `0` |
+| onClick | 点击标签时触发 | `(index: string \| number) => void` | `-` |
+| onChange | 当前激活的标签改变时触发 | `(index: string \| number) => void` | `-` |
+
+## SideBar.Item
+
+### Props
+
+| 属性 | 说明 | 类型 | 默认值 |
+| --- | --- | --- | --- |
+| title | 标题 | `string` | `-` |
+| value | 标签 Key , 匹配的标识符, 默认为索引值 | `string` \| `number` | `-` |
+| disabled | 是否禁用标签 | `boolean` | `false` |
+
+## 主题定制
+
+### 样式变量
+
+组件提供了下列 CSS 变量,可用于自定义样式,使用方法请参考 [ConfigProvider 组件](#/zh-CN/component/configprovider)。
+
+| 名称 | 说明 | 默认值 |
+| --- | --- | --- |
+| \--nutui-sidebar-background-color | 侧边栏导航背景色 | `$color-background` |
+| \--nutui-sidebar-border-radius | 侧边栏的圆角 | `0` |
+| \--nutui-sidebar-width | 侧边栏宽度 | `104px` |
+| \--nutui-sidebar-max-width | 侧边栏最大宽度 | `128` |
+| \--nutui-sidebar-title-height | 侧边栏标题高度 | `52px` |
+| \--nutui-sidebar-inactive-font-size | 普通状态下的字体大小 | `$font-size-base` |
+| \--nutui-sidebar-active-font-size | 激活状态下的字体大小 | `$font-size-l` |
+| \--nutui-sidebar-active-font-weight | 激活状态下的字重 | `$font-weight-bold` |
+| \--nutui-sidebar-active-color | 激活状态下的字体颜色 | `$color-primary` |
+| \--nutui-sidebar-item-background | 内容区域的背景色 | `$white` |
+| \--nutui-sidebar-item-padding | 内容区域的内边距 | `24px 20px` |
diff --git a/src/packages/sidebar/doc.zh-TW.md b/src/packages/sidebar/doc.zh-TW.md
new file mode 100644
index 000000000..52cb9ed79
--- /dev/null
+++ b/src/packages/sidebar/doc.zh-TW.md
@@ -0,0 +1,94 @@
+# SideBar組件
+
+用於側邊內容選擇和切換
+
+## 引入
+
+```tsx
+import { SideBar } from '@nutui/nutui-react'
+```
+
+## 示例代碼
+
+### 基礎用法
+
+:::demo
+
+
+
+:::
+
+### 禁用選項
+
+:::demo
+
+
+
+:::
+
+### 根據value匹配
+
+:::demo
+
+
+
+:::
+
+### 多個標題
+
+:::demo
+
+
+
+:::
+
+### 設置滾動動畫時長
+
+:::demo
+
+
+
+:::
+
+## SideBar
+
+### Props
+
+| 屬性 | 說明 | 類型 | 默認值 |
+| --- | --- | --- | --- |
+| value | 當前激活的`item`的key | `string \| number` | `-` |
+| defaultValue | 未設置value時,`item`的key的默認值 | `string \| number` | `-` |
+| contentDuration | 內容滾動動畫時長 | `number` | `0` |
+| sidebarDuration | 側欄滾動動畫時長 | `number` | `0` |
+| onClick | 點擊標簽時觸發 | `(index: string \| number) => void` | `-` |
+| onChange | 當前激活的標簽改變時觸發 | `(index: string \| number) => void` | `-` |
+
+## SideBar.Item
+
+### Props
+
+| 屬性 | 說明 | 類型 | 默認值 |
+| --- | --- | --- | --- |
+| title | 標題 | `string` | `-` |
+| value | 標簽 Key , 匹配的標識符, 默認為索引值 | `string` \| `number` | `-` |
+| disabled | 是否禁用標簽 | `boolean` | `false` |
+
+## 主題定製
+
+### 樣式變量
+
+組件提供了下列 CSS 變量,可用於自定義樣式,使用方法請參考 [ConfigProvider 組件](#/zh-CN/component/configprovider)。
+
+| 名稱 | 說明 | 默認值 |
+| --- | --- | --- |
+| \--nutui-sidebar-background-color | 側邊欄導航背景色 | `$color-background` |
+| \--nutui-sidebar-border-radius | 側邊欄的圓角 | `0` |
+| \--nutui-sidebar-width | 側邊欄寬度 | `104px` |
+| \--nutui-sidebar-max-width | 側邊欄最大寬度 | `128px` |
+| \--nutui-sidebar-title-height | 側邊欄標題高度 | `52px` |
+| \--nutui-sidebar-inactive-font-size | 普通狀態下的字體大小 | `$font-size-base` |
+| \--nutui-sidebar-active-font-size | 激活狀態下的字體大小 | `$font-size-l` |
+| \--nutui-sidebar-active-font-weight | 激活狀態下的字重 | `$font-weight-bold` |
+| \--nutui-sidebar-active-color | 激活狀態下的字體顏色 | `$color-primary` |
+| \--nutui-sidebar-item-background | 內容區域的背景色 | `$white` |
+| \--nutui-sidebar-item-padding | 內容區域的內邊距 | `24px 20px` |
diff --git a/src/packages/sidebar/index.taro.ts b/src/packages/sidebar/index.taro.ts
new file mode 100644
index 000000000..e7d26db3b
--- /dev/null
+++ b/src/packages/sidebar/index.taro.ts
@@ -0,0 +1,5 @@
+import { SideBar } from './sidebar.taro'
+
+export type { SideBarProps } from './types'
+
+export default SideBar
diff --git a/src/packages/sidebar/index.ts b/src/packages/sidebar/index.ts
new file mode 100644
index 000000000..105927bf4
--- /dev/null
+++ b/src/packages/sidebar/index.ts
@@ -0,0 +1,4 @@
+import { SideBar } from './sidebar'
+
+export type { SideBarProps } from './types'
+export default SideBar
diff --git a/src/packages/sidebar/sidebar.scss b/src/packages/sidebar/sidebar.scss
new file mode 100644
index 000000000..257bc57e7
--- /dev/null
+++ b/src/packages/sidebar/sidebar.scss
@@ -0,0 +1,62 @@
+@import '../../styles/mixins/index';
+@import '../sidebaritem/sidebaritem.scss';
+
+.nut-sidebar {
+ display: flex;
+ &-content {
+ flex-direction: column;
+ height: 100%;
+ &-wrap {
+ flex: 1;
+ overflow: hidden;
+ }
+ }
+ &-titles {
+ background: $sidebar-background-color;
+ flex-direction: column;
+ border-radius: $sidebar-border-radius;
+ height: 100%;
+ width: $sidebar-width;
+ max-width: $sidebar-max-width;
+ flex-shrink: 0;
+ &::-webkit-scrollbar {
+ display: none;
+ width: 0;
+ background: transparent;
+ }
+ .nut-sidebar-list {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ flex-shrink: 0;
+ }
+ &-scrollable {
+ overflow-x: hidden;
+ overflow-y: auto;
+ }
+ &-item {
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: $sidebar-title-height;
+ font-size: $sidebar-inactive-font-size;
+ color: $color-text;
+ &-text {
+ text-align: center;
+ white-space: normal;
+ width: $sidebar-width;
+ }
+ &-active .nut-sidebar-titles-item-text {
+ font-family: PingFangSC-Semibold;
+ color: $sidebar-active-color;
+ font-weight: $sidebar-active-font-weight;
+ font-size: $sidebar-active-font-size;
+ }
+ &-disabled {
+ color: $color-text-disabled;
+ cursor: not-allowed;
+ }
+ }
+ }
+}
diff --git a/src/packages/sidebar/sidebar.taro.tsx b/src/packages/sidebar/sidebar.taro.tsx
new file mode 100644
index 000000000..42d8020fd
--- /dev/null
+++ b/src/packages/sidebar/sidebar.taro.tsx
@@ -0,0 +1,236 @@
+import React, { FC, useEffect, useRef, useState } from 'react'
+import { ScrollView, View } from '@tarojs/components'
+import classNames from 'classnames'
+import Taro, { nextTick, createSelectorQuery } from '@tarojs/taro'
+import { ComponentDefaults } from '@/utils/typings'
+import { usePropsValue } from '@/utils/use-props-value'
+import { useForceUpdate } from '@/utils/use-force-update'
+import raf from '@/utils/raf'
+import useUuid from '@/utils/use-uuid'
+import { SideBarItemProps, SideBarProps } from './types'
+import SideBarItem from '@/packages/sidebaritem/index.taro'
+import { mergeProps } from '@/utils/merge-props'
+
+const defaultProps = {
+ ...ComponentDefaults,
+ contentDuration: 0,
+ sidebarDuration: 0,
+} as SideBarProps
+
+const classPrefix = 'nut-sidebar'
+export const SideBar: FC> & {
+ Item: typeof SideBarItem
+} = (props) => {
+ const {
+ contentDuration,
+ sidebarDuration,
+ children,
+ onClick,
+ onChange,
+ className,
+ ...rest
+ } = mergeProps(defaultProps, props)
+ const uuid = useUuid()
+ const [value, setValue] = usePropsValue({
+ value: props.value,
+ defaultValue: props.defaultValue,
+ finalValue: 0,
+ onChange,
+ })
+
+ const titleItemsRef = useRef([])
+ const navRef = useRef(null)
+
+ const getTitles = () => {
+ const titles: SideBarItemProps[] = []
+ React.Children.forEach(children, (child: any, idx) => {
+ if (React.isValidElement(child)) {
+ const props: any = child?.props
+ if (props?.title || props?.value) {
+ titles.push({
+ title: props.title,
+ value: props.value || idx,
+ disabled: props.disabled,
+ })
+ }
+ }
+ })
+ return titles
+ }
+
+ const titles = useRef(getTitles())
+ const forceUpdate = useForceUpdate()
+ useEffect(() => {
+ titles.current = getTitles()
+ let current: string | number = ''
+ titles.current.forEach((title) => {
+ if (title.value === value) {
+ current = value
+ }
+ })
+ forceUpdate()
+ }, [children])
+
+ const classes = classNames(classPrefix, className)
+ const classesTitle = classNames(
+ `${classPrefix}-titles`,
+ `${classPrefix}-titles-scrollable`
+ )
+ const getRect = (selector: string) => {
+ return new Promise((resolve) => {
+ createSelectorQuery()
+ .select(selector)
+ .boundingClientRect()
+ .exec((rect = []) => {
+ resolve(rect[0])
+ })
+ })
+ }
+ const getAllRect = (selector: string) => {
+ return new Promise((resolve) => {
+ createSelectorQuery()
+ .selectAll(selector)
+ .boundingClientRect()
+ .exec((rect = []) => {
+ resolve(rect[0])
+ })
+ })
+ }
+ type RectItem = {
+ bottom: number
+ dataset: { sid: string }
+ height: number
+ id: string
+ left: number
+ right: number
+ top: number
+ width: number
+ }
+ const scrollWithAnimation = useRef(false)
+ const navRectRef = useRef()
+ const titleRectRef = useRef([])
+ const [scrollTop, setScrollTop] = useState(0)
+ const scrollDirection = (to: number) => {
+ let count = 0
+ const frames = sidebarDuration === 0 ? 1 : Math.round(sidebarDuration / 16)
+ function animate() {
+ setScrollTop(to)
+ if (++count < frames) {
+ raf(animate)
+ }
+ }
+ animate()
+ }
+ const scrollIntoView = (index: number) => {
+ raf(() => {
+ Promise.all([
+ getRect(`#${classPrefix}-titles-${uuid} .${classPrefix}-list`),
+ getAllRect(
+ `#${classPrefix}-titles-${uuid} .${classPrefix}-titles-item`
+ ),
+ ]).then(([navRect, titleRects]: any) => {
+ navRectRef.current = navRect
+ titleRectRef.current = titleRects
+ const titleRect: RectItem = titleRectRef.current[index]
+ if (!titleRect) return
+ nextTick(() => {
+ scrollWithAnimation.current = true
+ })
+ scrollDirection(titleRect.height * (index - 1))
+ })
+ })
+ }
+
+ const getContentStyle = () => {
+ let index = titles.current.findIndex(
+ (t) => String(t.value) === String(value)
+ )
+ index = index < 0 ? 0 : index
+ return {
+ transform: `translate3d( 0,-${index * 100}%, 0)`,
+ transitionDuration: `${contentDuration}ms`,
+ }
+ }
+
+ useEffect(() => {
+ let index = titles.current.findIndex(
+ (t) => String(t.value) === String(value)
+ )
+ index = index < 0 ? 0 : index
+ scrollIntoView(index)
+ }, [value])
+
+ const tabChange = (item: SideBarItemProps, index: number) => {
+ if (item.disabled) return
+ onClick?.(item.value)
+ setValue(item.value)
+ }
+
+ return (
+
+
+
+ {titles.current.map((item, index) => {
+ return (
+ titleItemsRef.current.push(ref)}
+ id={`scrollIntoView${index}`}
+ onClick={(e) => {
+ tabChange(item, index)
+ }}
+ className={classNames(`${classPrefix}-titles-item`, {
+ [`${classPrefix}-titles-item-active`]:
+ !item.disabled && String(item.value) === String(value),
+ [`${classPrefix}-titles-item-disabled`]: item.disabled,
+ })}
+ key={item.value}
+ >
+
+ {item.title}
+
+
+ )
+ })}
+
+
+
+
+ {React.Children.map(children, (child, idx) => {
+ if (!React.isValidElement(child)) {
+ return null
+ }
+ let childProps = {
+ ...child.props,
+ active: value === child.props.value,
+ }
+ if (String(value) !== String(child.props.value ?? idx)) {
+ childProps = {
+ ...childProps,
+ }
+ }
+ return React.cloneElement(child, childProps)
+ })}
+
+
+
+ )
+}
+
+SideBar.displayName = 'NutSideBar'
+SideBar.Item = SideBarItem
diff --git a/src/packages/sidebar/sidebar.tsx b/src/packages/sidebar/sidebar.tsx
new file mode 100644
index 000000000..f6cd035a0
--- /dev/null
+++ b/src/packages/sidebar/sidebar.tsx
@@ -0,0 +1,173 @@
+import React, { FC, useEffect, useRef } from 'react'
+import classNames from 'classnames'
+import { ComponentDefaults } from '@/utils/typings'
+import SideBarItem from '@/packages/sidebaritem'
+import raf from '@/utils/raf'
+import { usePropsValue } from '@/utils/use-props-value'
+import { useForceUpdate } from '@/utils/use-force-update'
+import { mergeProps } from '@/utils/merge-props'
+
+import { SideBarItemProps, SideBarProps } from './types'
+
+const defaultProps = {
+ ...ComponentDefaults,
+ contentDuration: 0,
+ sidebarDuration: 0,
+} as SideBarProps
+
+const classPrefix = 'nut-sidebar'
+export const SideBar: FC> & {
+ Item: typeof SideBarItem
+} = (props) => {
+ const {
+ contentDuration,
+ sidebarDuration,
+ children,
+ onClick,
+ onChange,
+ className,
+ ...rest
+ } = mergeProps(defaultProps, props)
+
+ const [value, setValue] = usePropsValue({
+ value: props.value,
+ defaultValue: props.defaultValue,
+ finalValue: 0,
+ onChange,
+ })
+ const titleItemsRef = useRef([])
+ const navRef = useRef(null)
+ const scroll = (nav: any, to: number) => {
+ let count = 0
+ const from = nav.scrollTop
+ const frames = sidebarDuration === 0 ? 1 : Math.round(sidebarDuration / 16)
+ function animate() {
+ nav.scrollTop += (to - from) / frames
+ if (++count < frames) {
+ raf(animate)
+ }
+ }
+ animate()
+ }
+ const scrollIntoView = (index: number) => {
+ const nav = navRef.current
+ const titleItem = titleItemsRef.current
+ const titlesLength = titles.current.length
+ const itemLength = titleItemsRef.current.length
+ if (!nav || !titleItem || !titleItem[itemLength - titlesLength + index]) {
+ return
+ }
+ const title = titleItem[itemLength - titlesLength + index]
+ const runTop = title.offsetTop - nav.offsetTop + 10
+ const to =
+ runTop - (nav.offsetHeight - title.offsetHeight) / 2 + title.offsetHeight
+ scroll(nav, to)
+ }
+
+ const getTitles = () => {
+ const titles: SideBarItemProps[] = []
+ React.Children.forEach(children, (child: any, idx) => {
+ if (React.isValidElement(child)) {
+ const props: any = child?.props
+ if (props?.title || props?.value) {
+ titles.push({
+ title: props.title,
+ value: props.value ?? idx,
+ disabled: props.disabled,
+ })
+ }
+ }
+ })
+ return titles
+ }
+ const titles = useRef(getTitles())
+ const forceUpdate = useForceUpdate()
+ useEffect(() => {
+ titles.current = getTitles()
+ let current: string | number = ''
+ titles.current.forEach((title) => {
+ if (title.value === value) {
+ current = value
+ }
+ })
+ forceUpdate()
+ }, [children])
+
+ const classes = classNames(classPrefix, className)
+ const classesTitle = classNames(
+ `${classPrefix}-titles`,
+ `${classPrefix}-titles-scrollable`
+ )
+
+ const getContentStyle = () => {
+ let index = titles.current.findIndex((t) => t.value === value)
+ index = index < 0 ? 0 : index
+ return {
+ transform: `translate3d( 0,-${index * 100}%, 0)`,
+ transitionDuration: `${contentDuration}ms`,
+ }
+ }
+ useEffect(() => {
+ let index = titles.current.findIndex((t) => t.value === value)
+ index = index < 0 ? 0 : index
+ const rafId = requestAnimationFrame(() => {
+ scrollIntoView(index)
+ })
+ return () => cancelAnimationFrame(rafId)
+ }, [value])
+
+ const tabChange = (item: SideBarItemProps) => {
+ if (item.disabled) return
+ onClick?.(item.value)
+ setValue(item.value)
+ }
+ return (
+
+
+ {titles.current.map((item) => {
+ return (
+
{
+ tabChange(item)
+ }}
+ className={classNames(`${classPrefix}-titles-item`, {
+ [`${classPrefix}-titles-item-active`]:
+ !item.disabled && String(item.value) === String(value),
+ [`${classPrefix}-titles-item-disabled`]: item.disabled,
+ })}
+ ref={(ref: HTMLDivElement) => titleItemsRef.current.push(ref)}
+ key={item.value}
+ >
+
+ {item.title}
+
+
+ )
+ })}
+
+
+
+ {React.Children.map(children, (child, idx) => {
+ if (!React.isValidElement(child)) {
+ return null
+ }
+ let childProps = {
+ ...child.props,
+ active: value === child.props.value,
+ }
+
+ if (String(value) !== String(child.props.value || idx)) {
+ childProps = {
+ ...childProps,
+ }
+ }
+ return React.cloneElement(child, childProps)
+ })}
+
+
+
+ )
+}
+
+SideBar.displayName = 'NutSideBar'
+SideBar.Item = SideBarItem
diff --git a/src/packages/sidebar/types.ts b/src/packages/sidebar/types.ts
new file mode 100644
index 000000000..3bf5877ab
--- /dev/null
+++ b/src/packages/sidebar/types.ts
@@ -0,0 +1,18 @@
+import { BasicComponent } from '@/utils/typings'
+
+export type SideBarItemProps = {
+ title: string
+ disabled: boolean
+ active?: boolean
+ value: string | number
+}
+
+export interface SideBarProps extends BasicComponent {
+ value: string | number
+ defaultValue: string | number
+ contentDuration: number
+ sidebarDuration: number
+ onChange: (index: string | number) => void
+ onClick: (index: string | number) => void
+ children?: React.ReactNode
+}
diff --git a/src/packages/sidebar/utils.ts b/src/packages/sidebar/utils.ts
new file mode 100644
index 000000000..5f4d67d36
--- /dev/null
+++ b/src/packages/sidebar/utils.ts
@@ -0,0 +1,22 @@
+import { MouseEventHandler } from 'react'
+
+const handleClick: MouseEventHandler = (e) => {
+ e.stopPropagation()
+ const isIcon = (e.target as HTMLDivElement).className.includes('arrow-icon')
+ const isTitle =
+ (e.target as HTMLDivElement).className.includes('-title') || isIcon
+ const currentClass = e.currentTarget.className
+ const isShow = currentClass.includes('sidenavbar-show')
+ const arrowIcon = e.currentTarget.querySelector('.arrow-icon') as Element
+ const iconClass = arrowIcon.className
+
+ if (isTitle) {
+ e.currentTarget.className = isShow
+ ? currentClass.replace('sidenavbar-show', 'sidenavbar-hide')
+ : currentClass.replace('sidenavbar-hide', 'sidenavbar-show')
+ arrowIcon.className = isShow
+ ? iconClass.replace('arrow-down', 'arrow-up')
+ : iconClass.replace('arrow-up', 'arrow-down')
+ }
+}
+export { handleClick }
diff --git a/src/packages/sidebaritem/index.taro.ts b/src/packages/sidebaritem/index.taro.ts
new file mode 100644
index 000000000..26732133e
--- /dev/null
+++ b/src/packages/sidebaritem/index.taro.ts
@@ -0,0 +1,4 @@
+import { SideBarItem } from './sidebaritem.taro'
+
+export type { SideBarItemProps } from './sidebaritem.taro'
+export default SideBarItem
diff --git a/src/packages/sidebaritem/index.ts b/src/packages/sidebaritem/index.ts
new file mode 100644
index 000000000..dd3073a4a
--- /dev/null
+++ b/src/packages/sidebaritem/index.ts
@@ -0,0 +1,4 @@
+import { SideBarItem } from './sidebaritem'
+
+export type { SideBarItemProps } from './sidebaritem'
+export default SideBarItem
diff --git a/src/packages/sidebaritem/sidebaritem.scss b/src/packages/sidebaritem/sidebaritem.scss
new file mode 100644
index 000000000..3dc1602ae
--- /dev/null
+++ b/src/packages/sidebaritem/sidebaritem.scss
@@ -0,0 +1,16 @@
+.nut-sidebaritem {
+ width: 100%;
+ height: 100%;
+ flex-shrink: 0;
+ display: block;
+ background-color: $sidebar-item-background;
+ color: $color-title;
+ padding: $sidebar-item-padding;
+ box-sizing: border-box;
+ overflow: auto;
+
+ &.inactive {
+ overflow: visible;
+ height: 0;
+ }
+}
diff --git a/src/packages/sidebaritem/sidebaritem.taro.tsx b/src/packages/sidebaritem/sidebaritem.taro.tsx
new file mode 100644
index 000000000..8a1566ff8
--- /dev/null
+++ b/src/packages/sidebaritem/sidebaritem.taro.tsx
@@ -0,0 +1,37 @@
+import React, { FunctionComponent } from 'react'
+import classNames from 'classnames'
+import { View } from '@tarojs/components'
+import { mergeProps } from '@/utils/merge-props'
+
+export interface SideBarItemProps {
+ title: string | number
+ value: string | number
+ disabled: boolean
+ className: string
+ children?: React.ReactNode
+}
+
+const defaultProps = {
+ title: '',
+ value: '',
+ disabled: false,
+} as SideBarItemProps
+
+export const SideBarItem: FunctionComponent> = (
+ props
+) => {
+ const { children, className, disabled } = mergeProps(defaultProps, props)
+
+ const classPrefix = 'nut-sidebaritem'
+ const classes = classNames(
+ classPrefix,
+ {
+ active: !disabled && (props as any).active,
+ },
+ className
+ )
+
+ return children ? (
+ {!disabled && children}
+ ) : null
+}
diff --git a/src/packages/sidebaritem/sidebaritem.tsx b/src/packages/sidebaritem/sidebaritem.tsx
new file mode 100644
index 000000000..172b7cd60
--- /dev/null
+++ b/src/packages/sidebaritem/sidebaritem.tsx
@@ -0,0 +1,36 @@
+import React, { FunctionComponent } from 'react'
+import classNames from 'classnames'
+import { mergeProps } from '@/utils/merge-props'
+
+export interface SideBarItemProps {
+ title: string | number
+ value: string | number
+ disabled: boolean
+ className: string
+ children?: React.ReactNode
+}
+
+const defaultProps = {
+ title: '',
+ value: '',
+ disabled: false,
+} as SideBarItemProps
+
+export const SideBarItem: FunctionComponent> = (
+ props
+) => {
+ const { children, className, disabled } = mergeProps(defaultProps, props)
+
+ const classPrefix = 'nut-sidebaritem'
+ const classes = classNames(
+ classPrefix,
+ {
+ active: !disabled && (props as any).active,
+ },
+ className
+ )
+
+ return children ? (
+ {!disabled && children}
+ ) : null
+}
diff --git a/src/styles/variables.scss b/src/styles/variables.scss
index a1ff1f1bc..eeb37f857 100644
--- a/src/styles/variables.scss
+++ b/src/styles/variables.scss
@@ -2167,7 +2167,33 @@ $navbar-title-font-color: var(
--nutui-navbar-title-font-color,
$color-title
) !default;
-
+// sidebar(✅)
+$sidebar-background-color: var(
+ --nutui-sidebar-background-color,
+ $color-background
+) !default;
+$sidebar-border-radius: var(--nutui-sidebar-border-radius, 0) !default;
+$sidebar-width: var(--nutui-sidebar-width, 104px) !default;
+$sidebar-max-width: var(--nutui-sidebar-max-width, 128px) !default;
+$sidebar-title-height: var(--nutui-sidebar-title-height, 52px) !default;
+$sidebar-inactive-font-size: var(
+ --nutui-sidebar-inactive-font-size,
+ $font-size-base
+) !default;
+$sidebar-active-font-size: var(
+ --nutui-sidebar-active-font-size,
+ $font-size-l
+) !default;
+$sidebar-active-font-weight: var(
+ --nutui-sidebar-active-font-weight,
+ $font-weight-bold
+) !default;
+$sidebar-active-color: var(
+ --nutui-sidebar-active-color,
+ $color-primary
+) !default;
+$sidebar-item-background: var(--nutui-sidebar-item-background, $white) !default;
+$sidebar-item-padding: var(--nutui-sidebar-item-padding, 24px 20px) !default;
// sidenavbar(✅)
$sidenavbar-content-bg-color: var(
--nutui-sidenavbar-content-bg-color,