diff --git a/src/config.json b/src/config.json
index a4fc248f38..851ad769da 100644
--- a/src/config.json
+++ b/src/config.json
@@ -1053,6 +1053,18 @@
"v14": true,
"author": "lkjh3214"
},
+ {
+ "version": "3.0.0",
+ "name": "Segmented",
+ "type": "component",
+ "cName": "分段选择器",
+ "desc": "用于带页面类型区分的内容型卡片切换,如主图视频、图文切换。",
+ "sort": 6,
+ "show": true,
+ "taro": true,
+ "v14": true,
+ "author": "lkjh3214"
+ },
{
"version": "3.0.0",
"name": "Price",
diff --git a/src/packages/segmented/__tests__/segmented.spec.tsx b/src/packages/segmented/__tests__/segmented.spec.tsx
new file mode 100644
index 0000000000..8c10488d37
--- /dev/null
+++ b/src/packages/segmented/__tests__/segmented.spec.tsx
@@ -0,0 +1,47 @@
+import * as React from 'react'
+import { fireEvent, render } from '@testing-library/react'
+import '@testing-library/jest-dom'
+import { SegmentedItem, Segmented } from '@nutui/nutui-react'
+
+const defaultOptions = ['Daily', 'Weekly', 'Monthly']
+test('Simple Value Testing', () => {
+ const handleChange = vi.fn(() => {})
+ const { queryByText } = render(
+
+ )
+
+ expect(queryByText('Weekly')).toBeInTheDocument()
+
+ const secondItem = queryByText('Weekly')
+ if (secondItem) {
+ fireEvent.click(secondItem)
+ }
+ expect(handleChange).toBeCalled()
+})
+
+test('Complex Value Testing', () => {
+ const defaultOptions: SegmentedItem[] = [
+ {
+ label: 'Apps',
+ value: 'Apps',
+ },
+ {
+ label: 'AfterSaleService',
+ value: 'AfterSaleService',
+ },
+ ]
+ const handleChange = vi.fn(() => {})
+ const { container, queryByText } = render(
+
+ )
+
+ expect(queryByText('AfterSaleService')).toBeInTheDocument()
+})
diff --git a/src/packages/segmented/demo.taro.tsx b/src/packages/segmented/demo.taro.tsx
new file mode 100644
index 0000000000..b6e3206648
--- /dev/null
+++ b/src/packages/segmented/demo.taro.tsx
@@ -0,0 +1,33 @@
+import React from 'react'
+import { ScrollView, View } from '@tarojs/components'
+import Taro from '@tarojs/taro'
+import Demo1 from './demos/taro/demo1'
+import Demo2 from './demos/taro/demo2'
+import Demo3 from './demos/taro/demo3'
+import { useTranslate } from '@/sites/assets/locale/taro'
+import Header from '@/sites/components/header'
+
+const SegmentedDemo = () => {
+ const [translated] = useTranslate({
+ 'zh-CN': {
+ uncontrolled: '非受控',
+ controlled: '受控',
+ optionItems: '设置图标',
+ },
+ })
+ return (
+ <>
+
+
+ {translated.uncontrolled}
+
+ {translated.controlled}
+
+ {translated.optionItems}
+
+
+ >
+ )
+}
+
+export default SegmentedDemo
diff --git a/src/packages/segmented/demo.tsx b/src/packages/segmented/demo.tsx
new file mode 100644
index 0000000000..4036524e42
--- /dev/null
+++ b/src/packages/segmented/demo.tsx
@@ -0,0 +1,27 @@
+import React from 'react'
+import Demo1 from './demos/h5/demo1'
+import Demo2 from './demos/h5/demo2'
+import Demo3 from './demos/h5/demo3'
+import { useTranslate } from '@/sites/assets/locale'
+
+const SegmentedDemo = () => {
+ const [translated] = useTranslate({
+ 'zh-CN': {
+ uncontrolled: '非受控',
+ controlled: '受控',
+ optionItems: '设置图标',
+ },
+ })
+ return (
+
+
{translated.uncontrolled}
+
+ {translated.controlled}
+
+ {translated.optionItems}
+
+
+ )
+}
+
+export default SegmentedDemo
diff --git a/src/packages/segmented/demos/h5/demo1.tsx b/src/packages/segmented/demos/h5/demo1.tsx
new file mode 100644
index 0000000000..88e048aa6a
--- /dev/null
+++ b/src/packages/segmented/demos/h5/demo1.tsx
@@ -0,0 +1,19 @@
+import React from 'react'
+import { Segmented, Cell } from '@nutui/nutui-react'
+
+const defaultOptions = ['Daily', 'Weekly', 'Monthly']
+
+const Demo1 = () => {
+ return (
+
+ {
+ console.log('onChange', e)
+ }}
+ />
+ |
+ )
+}
+export default Demo1
diff --git a/src/packages/segmented/demos/h5/demo2.tsx b/src/packages/segmented/demos/h5/demo2.tsx
new file mode 100644
index 0000000000..5cefdbf4e9
--- /dev/null
+++ b/src/packages/segmented/demos/h5/demo2.tsx
@@ -0,0 +1,20 @@
+import React, { useState } from 'react'
+import { Segmented, Cell } from '@nutui/nutui-react'
+
+const defaultOptions = ['Daily', 'Weekly', 'Monthly']
+
+const Demo2 = () => {
+ const [value, setValue] = useState(1)
+ return (
+
+ {
+ setValue(val)
+ }}
+ />
+ |
+ )
+}
+export default Demo2
diff --git a/src/packages/segmented/demos/h5/demo3.tsx b/src/packages/segmented/demos/h5/demo3.tsx
new file mode 100644
index 0000000000..e69fd135ce
--- /dev/null
+++ b/src/packages/segmented/demos/h5/demo3.tsx
@@ -0,0 +1,32 @@
+import React, { useState } from 'react'
+import { Segmented, SegmentedItem, Cell } from '@nutui/nutui-react'
+import { AfterSaleService, Apps } from '@nutui/icons-react'
+
+const defaultOptions: SegmentedItem[] = [
+ {
+ label: 'Apps',
+ value: 'Apps',
+ icon: ,
+ },
+ {
+ label: 'AfterSaleService',
+ value: 'AfterSaleService',
+ icon: ,
+ },
+]
+
+const Demo3 = () => {
+ const [value, setValue] = useState('Apps')
+ return (
+
+ {
+ setValue(val)
+ }}
+ />
+ |
+ )
+}
+export default Demo3
diff --git a/src/packages/segmented/demos/taro/demo1.tsx b/src/packages/segmented/demos/taro/demo1.tsx
new file mode 100644
index 0000000000..cd27b95846
--- /dev/null
+++ b/src/packages/segmented/demos/taro/demo1.tsx
@@ -0,0 +1,19 @@
+import React from 'react'
+import { Segmented, Cell } from '@nutui/nutui-react-taro'
+
+const defaultOptions = ['Daily', 'Weekly', 'Monthly']
+
+const Demo1 = () => {
+ return (
+
+ {
+ console.log('onChange', e)
+ }}
+ />
+ |
+ )
+}
+export default Demo1
diff --git a/src/packages/segmented/demos/taro/demo2.tsx b/src/packages/segmented/demos/taro/demo2.tsx
new file mode 100644
index 0000000000..99e9fee4e6
--- /dev/null
+++ b/src/packages/segmented/demos/taro/demo2.tsx
@@ -0,0 +1,20 @@
+import React, { useState } from 'react'
+import { Segmented, Cell } from '@nutui/nutui-react-taro'
+
+const defaultOptions = ['Daily', 'Weekly', 'Monthly']
+
+const Demo2 = () => {
+ const [value, setValue] = useState(1)
+ return (
+
+ {
+ setValue(val)
+ }}
+ />
+ |
+ )
+}
+export default Demo2
diff --git a/src/packages/segmented/demos/taro/demo3.tsx b/src/packages/segmented/demos/taro/demo3.tsx
new file mode 100644
index 0000000000..81ce98b049
--- /dev/null
+++ b/src/packages/segmented/demos/taro/demo3.tsx
@@ -0,0 +1,32 @@
+import React, { useState } from 'react'
+import { Segmented, SegmentedItem, Cell } from '@nutui/nutui-react-taro'
+import { AfterSaleService, Apps } from '@nutui/icons-react-taro'
+
+const defaultOptions: SegmentedItem[] = [
+ {
+ label: 'Apps',
+ value: 'Apps',
+ icon: ,
+ },
+ {
+ label: 'AfterSaleService',
+ value: 'AfterSaleService',
+ icon: ,
+ },
+]
+
+const Demo3 = () => {
+ const [value, setValue] = useState('Apps')
+ return (
+
+ {
+ setValue(val)
+ }}
+ />
+ |
+ )
+}
+export default Demo3
diff --git a/src/packages/segmented/doc.en-US.md b/src/packages/segmented/doc.en-US.md
new file mode 100644
index 0000000000..acc286277f
--- /dev/null
+++ b/src/packages/segmented/doc.en-US.md
@@ -0,0 +1,74 @@
+# Segmented
+
+Used for switching content cards with page type distinctions, such as switching between main image videos and text with images.
+
+## Import
+
+```tsx
+import { Segmented } from '@nutui/nutui-react'
+```
+
+## Demo
+
+### Uncontrolled
+
+:::demo
+
+
+
+:::
+
+### Controlled
+
+:::demo
+
+
+
+:::
+
+### Icon
+
+:::demo
+
+
+
+:::
+
+## Segmented
+
+### Props
+
+| Property | Description | Type | Default |
+| --- | --- | --- | --- |
+| value | Currently selected value | `string \| number` | `-` |
+| defaultValue | Default selected value | `string \| number` | `-` |
+| options | Set children optional | `string[] \| number[]\| SegmentedItem[]` | `-` |
+| onChange | The callback function that is triggered when the state changes | `(value: string \| number) => void` | `-` |
+
+### SegmentedItem
+
+| Property | Description | Type | Default |
+| --- | --- | --- | --- |
+| label | Display text for Segmented item | `ReactNode` | `-` |
+| value | Value for Segmented item | `string \| number` | `-` |
+| disabled | Disabled state of segmented item | `boolean` | `false` |
+| icon | Display icon for Segmented item | `ReactNode` | `-` |
+| className | The additional css class | `string` | `-` |
+
+## 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 Value |
+| --- | --- | --- |
+| \--nutui-segmented-padding | Padding inside the segmented selector | `$spacing-xxxs` |
+| \--nutui-segmented-radius | Border radius of the segmented selector | `$radius-xs` |
+| \--nutui-segmented-background | Background color of the segmented selector | `$color-mask-part` |
+| \--nutui-segmented-item-padding | Padding for options in the segmented selector | `$spacing-xxs $spacing-xs` |
+| \--nutui-segmented-item-radius | Border radius for options in the segmented selector | `$spacing-xxs $spacing-xs` |
+| \--nutui-segmented-item-fontsize | Font size for options in the segmented selector | `$font-size-s` |
+| \--nutui-segmented-item-color | Text color for options in the segmented selector | `$color-primary-text` |
+| \--nutui-segmented-active-background | Background color for selected options in the segmented selector | `$color-mask-part` |
+| \--nutui-segmented-icon-margin-right | Spacing between options in the segmented selector | `$color-mask-part` |
diff --git a/src/packages/segmented/doc.md b/src/packages/segmented/doc.md
new file mode 100644
index 0000000000..bd7d7bd3f0
--- /dev/null
+++ b/src/packages/segmented/doc.md
@@ -0,0 +1,74 @@
+# Segmented 组件
+
+用于带页面类型区分的内容型卡片切换,如主图视频、图文切换。
+
+## 引入
+
+```tsx
+import { Segmented } from '@nutui/nutui-react'
+```
+
+## 示例代码
+
+### 非受控
+
+:::demo
+
+
+
+:::
+
+### 受控
+
+:::demo
+
+
+
+:::
+
+### 图标
+
+:::demo
+
+
+
+:::
+
+## Segmented
+
+### Props
+
+| 属性 | 说明 | 类型 | 默认值 |
+| --- | --- | --- | --- |
+| value | 当前选中的值 | `string \| number` | `-` |
+| defaultValue | 默认值 | `string \| number` | `-` |
+| options | 选项内容 | `string[] \| number[]\| SegmentedItem[]` | `-` |
+| onChange | 选项变化时的回调函数 | `(value: string \| number) => void` | `-` |
+
+### SegmentedItem
+
+| 属性 | 说明 | 类型 | 默认值 |
+| --- | --- | --- | --- |
+| label | 分段项的显示文本 | `ReactNode` | `-` |
+| value | 分段项的值 | `string \| number` | `-` |
+| disabled | 分段项的禁用状态 | `boolean` | `false` |
+| icon | 分段项的显示图标 | `ReactNode` | `-` |
+| className | 自定义类名 | `string` | `-` |
+
+## 主题定制
+
+### 样式变量
+
+组件提供了下列 CSS 变量,可用于自定义样式,使用方法请参考 [ConfigProvider 组件](#/zh-CN/component/configprovider)。
+
+| 名称 | 说明 | 默认值 |
+| --- | --- | --- |
+| \--nutui-segmented-padding | 分段选择器内边距 | `$spacing-xxxs` |
+| \--nutui-segmented-radius | 分段选择器圆角 | `$radius-xs` |
+| \--nutui-segmented-background | 分段选择器背景色 | `$color-mask-part` |
+| \--nutui-segmented-item-padding | 分段选择器选项内边距 | `$spacing-xxs $spacing-xs` |
+| \--nutui-segmented-item-radius | 分段选择器选项圆角 | `$spacing-xxs $spacing-xs` |
+| \--nutui-segmented-item-fontsize | 分段选择器选项字号 | `$font-size-s` |
+| \--nutui-segmented-item-color | 分段选择器选项文本颜色 | `$color-primary-text` |
+| \--nutui-segmented-active-background | 分段选择器选项选中的背景颜色 | `$color-mask-part` |
+| \--nutui-segmented-icon-margin-right | 分段选择器选项选间距 | `$color-mask-part` |
diff --git a/src/packages/segmented/doc.taro.md b/src/packages/segmented/doc.taro.md
new file mode 100644
index 0000000000..1339cb5266
--- /dev/null
+++ b/src/packages/segmented/doc.taro.md
@@ -0,0 +1,74 @@
+# Segmented 组件
+
+用于带页面类型区分的内容型卡片切换,如主图视频、图文切换。
+
+## 引入
+
+```tsx
+import { Segmented } from '@nutui/nutui-react-taro'
+```
+
+## 示例代码
+
+### 非受控
+
+:::demo
+
+
+
+:::
+
+### 受控
+
+:::demo
+
+
+
+:::
+
+### 图标
+
+:::demo
+
+
+
+:::
+
+## Segmented
+
+### Props
+
+| 属性 | 说明 | 类型 | 默认值 |
+| --- | --- | --- | --- |
+| value | 当前选中的值 | `string \| number` | `-` |
+| defaultValue | 默认值 | `string \| number` | `-` |
+| options | 选项内容 | `string[] \| number[]\| SegmentedItem[]` | `-` |
+| onChange | 选项变化时的回调函数 | `(value: string \| number) => void` | `-` |
+
+### SegmentedItem
+
+| 属性 | 说明 | 类型 | 默认值 |
+| --- | --- | --- | --- |
+| label | 分段项的显示文本 | `ReactNode` | `-` |
+| value | 分段项的值 | `string \| number` | `-` |
+| disabled | 分段项的禁用状态 | `boolean` | `false` |
+| icon | 分段项的显示图标 | `ReactNode` | `-` |
+| className | 自定义类名 | `string` | `-` |
+
+## 主题定制
+
+### 样式变量
+
+组件提供了下列 CSS 变量,可用于自定义样式,使用方法请参考 [ConfigProvider 组件](#/zh-CN/component/configprovider)。
+
+| 名称 | 说明 | 默认值 |
+| --- | --- | --- |
+| \--nutui-segmented-padding | 分段选择器内边距 | `$spacing-xxxs` |
+| \--nutui-segmented-radius | 分段选择器圆角 | `$radius-xs` |
+| \--nutui-segmented-background | 分段选择器背景色 | `$color-mask-part` |
+| \--nutui-segmented-item-padding | 分段选择器选项内边距 | `$spacing-xxs $spacing-xs` |
+| \--nutui-segmented-item-radius | 分段选择器选项圆角 | `$spacing-xxs $spacing-xs` |
+| \--nutui-segmented-item-fontsize | 分段选择器选项字号 | `$font-size-s` |
+| \--nutui-segmented-item-color | 分段选择器选项文本颜色 | `$color-primary-text` |
+| \--nutui-segmented-active-background | 分段选择器选项选中的背景颜色 | `$color-mask-part` |
+| \--nutui-segmented-icon-margin-right | 分段选择器选项选间距 | `$color-mask-part` |
diff --git a/src/packages/segmented/doc.zh-TW.md b/src/packages/segmented/doc.zh-TW.md
new file mode 100644
index 0000000000..af4a447c43
--- /dev/null
+++ b/src/packages/segmented/doc.zh-TW.md
@@ -0,0 +1,74 @@
+# Segmented 组件
+
+用於帶頁面類型區分的內容型卡片切換,如主圖視頻、圖文切換。
+
+## 引入
+
+```tsx
+import { Segmented } from '@nutui/nutui-react'
+```
+
+## 示例代碼
+
+### 非受控
+
+:::demo
+
+
+
+:::
+
+### 受控
+
+:::demo
+
+
+
+:::
+
+### 圖標
+
+:::demo
+
+
+
+:::
+
+## Segmented
+
+### Props
+
+| 屬性 | 說明 | 類型 | 默認值 |
+| --- | --- | --- | --- |
+| value | 目前選取的值 | `string \| number` | `-` |
+| defaultValue | 預設值 | `string \| number` | `-` |
+| options | 選項內容 | `string[] \| number[]\| SegmentedItem[]` | `-` |
+| onChange | 選項變化時的回調函數 | `(value: string \| number) => void` | `-` |
+
+### SegmentedItem
+
+| 屬性 | 說明 | 類型 | 默認值 |
+| --- | --- | --- | --- |
+| label | 分段項目的顯示文字 | `ReactNode` | `-` |
+| value | 分段項的值 | `string \| number` | `-` |
+| disabled | 分段項目的禁用狀態 | `boolean` | `false` |
+| icon | 分段項目的顯示圖標 | `ReactNode` | `-` |
+| className | 自訂類別名 | `string` | `-` |
+
+## 主題定制
+
+### 樣式變量
+
+組件提供了下列 CSS 變量,可用於自定義樣式,使用方法請參考 [ConfigProvider 組件](#/zh-CN/component/configprovider)。
+
+| 名稱 | 說明 | 默認值 |
+| --- | --- | --- |
+| \--nutui-segmented-padding | 分段選擇器內邊距 | `$spacing-xxxs` |
+| \--nutui-segmented-radius | 分段選擇器圓角 | `$radius-xs` |
+| \--nutui-segmented-background | 分段選擇器背景色 | `$color-mask-part` |
+| \--nutui-segmented-item-padding | 分段選擇器選項內邊距 | `$spacing-xxs $spacing-xs` |
+| \--nutui-segmented-item-radius | 分段選擇器選項圓角 | `$spacing-xxs $spacing-xs` |
+| \--nutui-segmented-item-fontsize | 分段選擇器選項字號 | `$font-size-s` |
+| \--nutui-segmented-item-color | 分段選擇器選項文字顏色 | `$color-primary-text` |
+| \--nutui-segmented-active-background | 分段選擇器選項選取的背景顏色 | `$color-mask-part` |
+| \--nutui-segmented-icon-margin-right | 分段選擇器選項選間距 | `$color-mask-part` |
diff --git a/src/packages/segmented/index.taro.ts b/src/packages/segmented/index.taro.ts
new file mode 100644
index 0000000000..fc5ab2dad8
--- /dev/null
+++ b/src/packages/segmented/index.taro.ts
@@ -0,0 +1,4 @@
+import { Segmented } from './segmented.taro'
+
+export type { SegmentedProps, SegmentedItem } from './types'
+export default Segmented
diff --git a/src/packages/segmented/index.ts b/src/packages/segmented/index.ts
new file mode 100644
index 0000000000..0592a06bef
--- /dev/null
+++ b/src/packages/segmented/index.ts
@@ -0,0 +1,4 @@
+import { Segmented } from './segmented'
+
+export type { SegmentedProps, SegmentedItem } from './types'
+export default Segmented
diff --git a/src/packages/segmented/segmented.scss b/src/packages/segmented/segmented.scss
new file mode 100644
index 0000000000..9163ff2dff
--- /dev/null
+++ b/src/packages/segmented/segmented.scss
@@ -0,0 +1,38 @@
+.nut-segmented {
+ display: inline-flex;
+ height: $segmented-height;
+ min-width: 24px;
+ padding: $segmented-padding;
+ align-items: center;
+ border-radius: $segmented-border-radius;
+ background: $color-mask-part;
+ box-sizing: border-box;
+}
+
+.nut-segmented-item {
+ display: flex;
+ height: $segmented-item-height;
+ align-items: center;
+ justify-content: center;
+ padding: $segmented-item-padding;
+ border-radius: $segmented-item-radius;
+ color: $segmented-item-color;
+ font-size: $segmented-item-fontsize;
+ line-height: 1;
+ box-sizing: border-box;
+}
+
+.nut-segmented-item-active {
+ background: $segmented-active-background;
+}
+
+.nut-segmented-icon {
+ height: 10px;
+ width: 10px;
+ margin-right: $segmented-icon-margin-right;
+ .nut-icon {
+ height: 10px;
+ width: 10px;
+ font-size: 10px;
+ }
+}
diff --git a/src/packages/segmented/segmented.taro.tsx b/src/packages/segmented/segmented.taro.tsx
new file mode 100644
index 0000000000..75edfb8df5
--- /dev/null
+++ b/src/packages/segmented/segmented.taro.tsx
@@ -0,0 +1,81 @@
+import React, { useCallback } from 'react'
+import classNames from 'classnames'
+import { View } from '@tarojs/components'
+import { SegmentedItem, SegmentedProps } from './types'
+import { usePropsValue } from '@/utils/use-props-value'
+import { mergeProps } from '@/utils/merge-props'
+
+const defaultProps = {
+ options: [],
+ onChange: (value: T) => {},
+}
+
+export const Segmented = (props: Partial) => {
+ const classPrefix = 'nut-segmented'
+ const itemClassPrefix = 'nut-segmented-item'
+ const mergedProps = mergeProps(defaultProps, props)
+
+ const { options, onChange } = mergedProps
+
+ const [value, setValue] = usePropsValue({
+ value: mergedProps.value,
+ defaultValue: mergedProps.defaultValue,
+ finalValue: 0,
+ onChange: mergedProps.onChange,
+ })
+ const renderItems = useCallback(
+ (options: SegmentedProps['options'], value: string | number) => {
+ return options.map((option, index) => {
+ const optionType = typeof option
+ switch (optionType) {
+ case 'object':
+ // eslint-disable-next-line no-case-declarations
+ const opt = option as SegmentedItem
+ return (
+ {
+ if (opt.disabled) return
+ setValue((option as SegmentedItem).value)
+ }}
+ >
+ {opt.icon ? (
+ {opt.icon}
+ ) : null}
+ {opt.label}
+
+ )
+ default: {
+ if (typeof option !== 'string' && typeof option !== 'number') {
+ console.warn('Unsupported option type:', optionType)
+ }
+ return (
+ {
+ setValue(index)
+ }}
+ >
+ {option as string}
+
+ )
+ }
+ }
+ })
+ },
+ [value]
+ )
+
+ return (
+
+ {renderItems(options, value)}
+
+ )
+}
diff --git a/src/packages/segmented/segmented.tsx b/src/packages/segmented/segmented.tsx
new file mode 100644
index 0000000000..654d1f5fb1
--- /dev/null
+++ b/src/packages/segmented/segmented.tsx
@@ -0,0 +1,80 @@
+import React, { useCallback } from 'react'
+import classNames from 'classnames'
+import { SegmentedItem, SegmentedProps } from './types'
+import { usePropsValue } from '../../utils/use-props-value'
+import { mergeProps } from '@/utils/merge-props'
+
+const defaultProps = {
+ options: [],
+ onChange: (value: T) => {},
+}
+
+export const Segmented = (props: Partial) => {
+ const classPrefix = 'nut-segmented'
+ const itemClassPrefix = 'nut-segmented-item'
+ const mergedProps = mergeProps(defaultProps, props)
+
+ const { options, onChange } = mergedProps
+
+ const [value, setValue] = usePropsValue({
+ value: mergedProps.value,
+ defaultValue: mergedProps.defaultValue,
+ finalValue: 0,
+ onChange: mergedProps.onChange,
+ })
+ const renderItems = useCallback(
+ (options: SegmentedProps['options'], value: string | number) => {
+ return options.map((option, index) => {
+ const optionType = typeof option
+ switch (optionType) {
+ case 'object':
+ // eslint-disable-next-line no-case-declarations
+ const opt = option as SegmentedItem
+ return (
+ {
+ if (opt.disabled) return
+ setValue(opt.value)
+ }}
+ >
+ {opt.icon ? (
+ {opt.icon}
+ ) : null}
+ {opt.label}
+
+ )
+ default: {
+ if (typeof option !== 'string' && typeof option !== 'number') {
+ console.warn('Unsupported option type:', optionType)
+ }
+ return (
+ {
+ setValue(index)
+ }}
+ >
+ {option as string}
+
+ )
+ }
+ }
+ })
+ },
+ [value]
+ )
+
+ return (
+
+ {renderItems(options, value)}
+
+ )
+}
diff --git a/src/packages/segmented/types.ts b/src/packages/segmented/types.ts
new file mode 100644
index 0000000000..f156d1a11f
--- /dev/null
+++ b/src/packages/segmented/types.ts
@@ -0,0 +1,17 @@
+import { ReactNode } from 'react'
+import { BasicComponent } from '@/utils/typings'
+
+export interface SegmentedItem {
+ label: ReactNode
+ value: string | number
+ icon?: ReactNode
+ disabled?: boolean
+ className?: string
+}
+
+export interface SegmentedProps extends BasicComponent {
+ options: string[] | number[] | SegmentedItem[]
+ value: string | number
+ defaultValue: string | number
+ onChange: (value: string | number) => void
+}
diff --git a/src/styles/variables.scss b/src/styles/variables.scss
index b445e4a88c..f0ec31c62b 100644
--- a/src/styles/variables.scss
+++ b/src/styles/variables.scss
@@ -1969,6 +1969,37 @@ $searchbar-input-text-align: var(
left
) !default;
+// segmented
+$segmented-height: var(--nutui-segmented-height, 24px) !default;
+$segmented-padding: var(--nutui-segmented-padding, $spacing-xxxs) !default;
+$segmented-border-radius: var(--nutui-segmented-radius, $radius-xs) !default;
+$segmented-background: var(
+ --nutui-segmented-background,
+ $color-mask-part
+) !default;
+$segmented-item-height: var(--nutui-segmented-height, 20px) !default;
+$segmented-item-padding: var(
+ --nutui-segmented-item-padding,
+ 0 $spacing-xs
+) !default;
+$segmented-item-radius: var(--nutui-segmented-item-radius, $radius-xs) !default;
+$segmented-item-fontsize: var(
+ --nutui-segmented-item-fontsize,
+ $font-size-s
+) !default;
+$segmented-item-color: var(
+ --nutui-segmented-item-color,
+ $color-primary-text
+) !default;
+$segmented-active-background: var(
+ --nutui-segmented-active-background,
+ $color-mask-part
+) !default;
+$segmented-icon-margin-right: var(
+ --nutui-segmented-icon-margin-right,
+ $spacing-xxxs
+) !default;
+
// empty(✅)
$empty-padding: var(--nutui-empty-padding, 32px 40px) !default;
$empty-image-size: var(--nutui-empty-image-size, 160px) !default;