feat(components): [segmented] new component (#16258)

* feat(components): [segmented] new component

* feat(components): [segmented]

* feat(components): update

* feat(components): update

* feat(theme-chalk): update

* feat(components): update

* feat: update

* feat: update

* feat(components): add focus-visible

* feat(theme-chalk): update

* feat(components): fix test

* docs: docs

* feat(components): update

* feat: add icon
This commit is contained in:
kooriookami 2024-04-11 18:48:52 +08:00 committed by GitHub
parent d0eb6c3d1a
commit 546b21ea82
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 959 additions and 0 deletions

View File

@ -246,6 +246,11 @@
"link": "/statistic",
"text": "Statistic",
"promotion": "2.2.30"
},
{
"link": "/segmented",
"text": "Segmented",
"promotion": "2.7.0"
}
]
},

View File

@ -49,6 +49,7 @@ import OvTour from './ov-tour.vue'
import OvTree from './ov-tree.vue'
import OvTreeSelect from './ov-tree-select.vue'
import OvStatistic from './ov-statistic.vue'
import OvSegmented from './ov-segmented.vue'
import OvAffix from './ov-affix.vue'
import OvAnchor from './ov-anchor.vue'
import OvBacktop from './ov-backtop.vue'
@ -126,6 +127,7 @@ export default {
'tree-select': OvTreeSelect,
'tree-v2': OvTree,
statistic: OvStatistic,
segmented: OvSegmented,
affix: OvAffix,
anchor: OvAnchor,
backtop: OvBacktop,

View File

@ -0,0 +1,91 @@
<template>
<svg
width="280"
height="180"
viewBox="0 0 280 180"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect width="280" height="180" fill="var(--el-fill-color-light)" />
<g filter="url(#filter0_d_15674_94)">
<rect
x="56"
y="74"
width="168"
height="28"
rx="4"
fill="var(--el-fill-color-blank)"
/>
<rect
x="55.7"
y="73.7"
width="168.6"
height="28.6"
rx="4.3"
stroke="var(--el-border-color-dark)"
stroke-width="0.6"
/>
</g>
<rect
x="124"
y="84"
width="32"
height="8"
rx="4"
fill="var(--el-color-primary)"
/>
<rect
x="80"
y="84"
width="32"
height="8"
rx="4"
fill="var(--el-border-color-dark)"
/>
<rect
x="168"
y="84"
width="32"
height="8"
rx="4"
fill="var(--el-border-color-dark)"
/>
<defs>
<filter
id="filter0_d_15674_94"
x="42.0666"
y="60.0671"
width="195.867"
height="55.8659"
filterUnits="userSpaceOnUse"
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feColorMatrix
in="SourceAlpha"
type="matrix"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
result="hardAlpha"
/>
<feOffset />
<feGaussianBlur stdDeviation="6.66667" />
<feComposite in2="hardAlpha" operator="out" />
<feColorMatrix
type="matrix"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.05 0"
/>
<feBlend
mode="normal"
in2="BackgroundImageFix"
result="effect1_dropShadow_15674_94"
/>
<feBlend
mode="normal"
in="SourceGraphic"
in2="effect1_dropShadow_15674_94"
result="shape"
/>
</filter>
</defs>
</svg>
</template>

View File

@ -92,6 +92,7 @@ declare module '@vue/runtime-core' {
OvRate: typeof import('./.vitepress/vitepress/components/overview-icons/ov-rate.vue')['default']
OvResult: typeof import('./.vitepress/vitepress/components/overview-icons/ov-result.vue')['default']
OvScrollbar: typeof import('./.vitepress/vitepress/components/overview-icons/ov-scrollbar.vue')['default']
OvSegmented: typeof import('./.vitepress/vitepress/components/overview-icons/ov-segmented.vue')['default']
OvSelect: typeof import('./.vitepress/vitepress/components/overview-icons/ov-select.vue')['default']
OvSelectV2: typeof import('./.vitepress/vitepress/components/overview-icons/ov-select-v2.vue')['default']
OvSkeleton: typeof import('./.vitepress/vitepress/components/overview-icons/ov-skeleton.vue')['default']

View File

@ -0,0 +1,102 @@
---
title: Segmented
lang: en-US
---
# Segmented
Display multiple options and allow users to select a single option.
## Basic Usage
Set `v-model` to the option value is selected.
:::demo
segmented/basic
:::
## Disabled
Set `disabled` of segmented or option to `true` to disable it.
:::demo
segmented/disabled
:::
## Block
Set `block` to `true` to fit the width of parent element.
:::demo
segmented/block
:::
## Custom Content
Set default slot to render custom content.
:::demo
segmented/custom-content
:::
## Custom Style
Set default slot to render custom content.
:::demo
segmented/custom-style
:::
## API
### Attributes
| Name | Description | Type | Default |
|-----------------------|------------------------------------|------------------------------------------------|---------|
| model-value / v-model | binding value | ^[string] / ^[number] | — |
| options | data of the options | ^[array]`Option[]` | [] |
| size | size of component | ^[enum]`'' \| 'large' \| 'default' \| 'small'` | '' |
| block | fit width of parent content | ^[boolean] | — |
| disabled | whether segmented is disabled | ^[boolean] | false |
| validate-event | whether to trigger form validation | ^[boolean] | true |
| name | native `name` attribute | ^[string] | — |
| id | native `id` attribute | ^[string] | — |
| aria-label ^(a11y) | native `aria-label` attribute | ^[string] | — |
### Events
| Name | Description | Type |
|--------|-------------------------------------------------------------------------------|---------------------------------|
| change | triggers when the selected value changes, the param is current selected value | ^[Function]`(val: any) => void` |
### Slots
| Name | Description |
|---------|-----------------|
| default | option renderer |
## Type Declarations
<details>
<summary>Show declarations</summary>
```ts
type Option = {
label: string
value: string | number | boolean
disabled?: boolean
[key: string]: any
} | string | number | boolean | undefined
```
</details>

View File

@ -54,6 +54,9 @@
<el-form-item label="Instant delivery" prop="delivery">
<el-switch v-model="ruleForm.delivery" />
</el-form-item>
<el-form-item label="Activity location" prop="location">
<el-segmented v-model="ruleForm.location" :options="locationOptions" />
</el-form-item>
<el-form-item label="Activity type" prop="type">
<el-checkbox-group v-model="ruleForm.type">
<el-checkbox value="Online activities" name="type">
@ -99,6 +102,7 @@ interface RuleForm {
date1: string
date2: string
delivery: boolean
location: string
type: string[]
resource: string
desc: string
@ -113,11 +117,14 @@ const ruleForm = reactive<RuleForm>({
date1: '',
date2: '',
delivery: false,
location: '',
type: [],
resource: '',
desc: '',
})
const locationOptions = ['Home', 'Company', 'School']
const rules = reactive<FormRules<RuleForm>>({
name: [
{ required: true, message: 'Please input Activity name', trigger: 'blur' },
@ -153,6 +160,13 @@ const rules = reactive<FormRules<RuleForm>>({
trigger: 'change',
},
],
location: [
{
required: true,
message: 'Please select a location',
trigger: 'change',
},
],
type: [
{
type: 'array',

View File

@ -0,0 +1,14 @@
<template>
<div class="flex flex-col items-start gap-4">
<el-segmented v-model="value" :options="options" size="large" />
<el-segmented v-model="value" :options="options" size="default" />
<el-segmented v-model="value" :options="options" size="small" />
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const value = ref('Mon')
const options = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
</script>

View File

@ -0,0 +1,20 @@
<template>
<div>
<el-segmented v-model="value" :options="options" block />
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const value = ref('Mon')
const options = [
'Mon',
'Tue',
'Wed',
'Thu',
'Fri',
'Sat',
'Sunday long long long long long long long',
]
</script>

View File

@ -0,0 +1,60 @@
<template>
<div>
<el-segmented v-model="value" :options="options">
<template #default="{ item }">
<div class="flex flex-col items-center gap-2 p-2">
<el-icon size="20">
<component :is="item.icon" />
</el-icon>
<div>{{ item.label }}</div>
</div>
</template>
</el-segmented>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import {
Apple,
Cherry,
Grape,
Orange,
Pear,
Watermelon,
} from '@element-plus/icons-vue'
const value = ref('Apple')
const options = [
{
label: 'Apple',
value: 'Apple',
icon: Apple,
},
{
label: 'Cherry',
value: 'Cherry',
icon: Cherry,
},
{
label: 'Grape',
value: 'Grape',
icon: Grape,
},
{
label: 'Orange',
value: 'Orange',
icon: Orange,
},
{
label: 'Pear',
value: 'Pear',
icon: Pear,
},
{
label: 'Watermelon',
value: 'Watermelon',
icon: Watermelon,
},
]
</script>

View File

@ -0,0 +1,20 @@
<template>
<div class="custom-style">
<el-segmented v-model="value" :options="options" />
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const value = ref('Delicacy')
const options = ['Delicacy', 'Desserts&Drinks', 'Fresh foods', 'Supermarket']
</script>
<style scoped>
.custom-style .el-segmented {
--el-segmented-item-selected-color: var(--el-text-color-primary);
--el-segmented-item-selected-bg-color: #ffd100;
--el-border-radius-base: 16px;
}
</style>

View File

@ -0,0 +1,45 @@
<template>
<div class="flex flex-col items-start gap-4">
<el-segmented v-model="value" :options="options" disabled />
<el-segmented v-model="value" :options="options" />
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const value = ref('Mon')
const options = [
{
label: 'Mon',
value: 'Mon',
disabled: true,
},
{
label: 'Tue',
value: 'Tue',
},
{
label: 'Wed',
value: 'Wed',
disabled: true,
},
{
label: 'Thu',
value: 'Thu',
},
{
label: 'Fri',
value: 'Fri',
disabled: true,
},
{
label: 'Sat',
value: 'Sat',
},
{
label: 'Sun',
value: 'Sun',
},
]
</script>

View File

@ -72,6 +72,7 @@ export * from './virtual-list'
export * from './watermark'
export * from './tour'
export * from './anchor'
export * from './segmented'
// plugins
export * from './infinite-scroll'

View File

@ -0,0 +1,136 @@
import { nextTick, ref } from 'vue'
import { mount } from '@vue/test-utils'
import { describe, expect, test } from 'vitest'
import Segmented from '../src/segmented.vue'
describe('Segmented.vue', () => {
test('render test', async () => {
const value = ref('Mon')
const options = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
const wrapper = mount(() => (
<Segmented v-model={value.value} options={options}></Segmented>
))
await nextTick()
expect(wrapper.find('.is-selected').text()).toEqual('Mon')
})
test('render v-model', async () => {
const value = ref('Mon')
const options = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
const wrapper = mount(() => (
<Segmented v-model={value.value} options={options}></Segmented>
))
expect(wrapper.find('.is-selected').text()).toEqual('Mon')
value.value = 'Tue'
await nextTick()
expect(wrapper.find('.is-selected').text()).toEqual('Tue')
})
test('render options', async () => {
const value = ref('Mon')
const options = ref(['a', 'b'])
const wrapper = mount(() => (
<Segmented v-model={value.value} options={options.value}></Segmented>
))
await nextTick()
expect(wrapper.findAll('.el-segmented__item').length).toEqual(2)
options.value.push('c')
await nextTick()
expect(wrapper.findAll('.el-segmented__item').length).toEqual(3)
})
test('render block', async () => {
const value = ref('Mon')
const options = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
const wrapper = mount(() => (
<Segmented v-model={value.value} options={options} block></Segmented>
))
await nextTick()
expect(wrapper.find('.is-block').exists()).toBe(true)
})
test('render size', async () => {
const value = ref('Mon')
const options = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
const wrapper = mount(() => (
<Segmented
v-model={value.value}
options={options}
size={'large'}
></Segmented>
))
await nextTick()
expect(wrapper.find('.el-segmented--large').exists()).toBe(true)
})
test('render disabled', async () => {
const value = ref('Mon')
const options = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
const wrapper = mount(() => (
<Segmented v-model={value.value} options={options} disabled></Segmented>
))
await nextTick()
expect(wrapper.findAll('.is-disabled').length).toBe(7)
})
test('render option disabled', async () => {
const value = ref('Mon')
const options = [
{
label: 'Mon',
value: 'Mon',
disabled: true,
},
{
label: 'Tue',
value: 'Tue',
},
{
label: 'Wed',
value: 'Wed',
disabled: true,
},
{
label: 'Thu',
value: 'Thu',
},
{
label: 'Fri',
value: 'Fri',
disabled: true,
},
{
label: 'Sat',
value: 'Sat',
},
{
label: 'Sun',
value: 'Sun',
},
]
const wrapper = mount(() => (
<Segmented v-model={value.value} options={options}></Segmented>
))
await nextTick()
expect(wrapper.findAll('.is-disabled').length).toBe(3)
})
test('render accessible attributes', async () => {
const value = ref('Mon')
const options = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
const wrapper = mount(() => (
<Segmented
v-model={value.value}
options={options}
id={'id'}
name={'name'}
aria-label={'label'}
></Segmented>
))
const input = wrapper.find('input')
await nextTick()
expect(wrapper.attributes('id')).toEqual('id')
expect(wrapper.attributes('aria-label')).toEqual('label')
expect(input.attributes('name')).toEqual('name')
})
})

View File

@ -0,0 +1,7 @@
import { withInstall } from '@element-plus/utils'
import Segmented from './src/segmented.vue'
export const ElSegmented = withInstall(Segmented)
export default ElSegmented
export * from './src/segmented'

View File

@ -0,0 +1,70 @@
import {
buildProps,
definePropType,
isNumber,
isString,
} from '@element-plus/utils'
import { useSizeProp } from '@element-plus/hooks'
import { CHANGE_EVENT, UPDATE_MODEL_EVENT } from '@element-plus/constants'
import type { Option } from './types'
import type { ExtractPropTypes } from 'vue'
import type Segmented from './segmented.vue'
export const segmentedProps = buildProps({
/**
* @description options of segmented
*/
options: {
type: definePropType<Option[]>(Array),
default: () => [],
},
/**
* @description binding value
*/
modelValue: {
type: [String, Number, Boolean],
default: undefined,
},
/**
* @description fit width of parent content
*/
block: Boolean,
/**
* @description size of component
*/
size: useSizeProp,
/**
* @description whether segmented is disabled
*/
disabled: Boolean,
/**
* @description whether to trigger form validation
*/
validateEvent: {
type: Boolean,
default: true,
},
/**
* @description native input id
*/
id: String,
/**
* @description native `name` attribute
*/
name: String,
/**
* @description native `aria-label` attribute
*/
ariaLabel: String,
})
export type SegmentedProps = ExtractPropTypes<typeof segmentedProps>
export const segmentedEmits = {
[UPDATE_MODEL_EVENT]: (val: any) => isString(val) || isNumber(val),
[CHANGE_EVENT]: (val: any) => isString(val) || isNumber(val),
}
export type SegmentedEmits = typeof segmentedEmits
export type SegmentedInstance = InstanceType<typeof Segmented>

View File

@ -0,0 +1,173 @@
<template>
<div
:id="inputId"
ref="segmentedRef"
:class="segmentedCls"
role="radiogroup"
:aria-label="!isLabeledByFormItem ? ariaLabel || 'segmented' : undefined"
:aria-labelledby="isLabeledByFormItem ? formItem!.labelId : undefined"
>
<div :class="ns.e('group')">
<div :style="selectedStyle" :class="selectedCls" />
<label
v-for="(item, index) in options"
:key="index"
:class="getItemCls(item)"
>
<input
:class="ns.e('item-input')"
type="radio"
:name="name"
:disabled="getDisabled(item)"
:checked="getSelected(item)"
@change="handleChange(item)"
/>
<div :class="ns.e('item-label')">
<slot :item="item">{{ getLabel(item) }}</slot>
</div>
</label>
</div>
</div>
</template>
<script lang="ts" setup>
import { computed, reactive, ref, watch } from 'vue'
import { useActiveElement, useResizeObserver } from '@vueuse/core'
import { useId, useNamespace } from '@element-plus/hooks'
import {
useFormDisabled,
useFormItem,
useFormItemInputId,
useFormSize,
} from '@element-plus/components/form'
import { debugWarn, isObject } from '@element-plus/utils'
import { CHANGE_EVENT, UPDATE_MODEL_EVENT } from '@element-plus/constants'
import { segmentedEmits, segmentedProps } from './segmented'
import type { Option } from './types'
defineOptions({
name: 'ElSegmented',
})
const props = defineProps(segmentedProps)
const emit = defineEmits(segmentedEmits)
const ns = useNamespace('segmented')
const segmentedId = useId()
const segmentedSize = useFormSize()
const _disabled = useFormDisabled()
const { formItem } = useFormItem()
const { inputId, isLabeledByFormItem } = useFormItemInputId(props, {
formItemContext: formItem,
})
const segmentedRef = ref<HTMLElement | null>(null)
const activeElement = useActiveElement()
const state = reactive({
isInit: false,
width: 0,
translateX: 0,
disabled: false,
focusVisible: false,
})
const handleChange = (item: Option) => {
const value = getValue(item)
emit(UPDATE_MODEL_EVENT, value)
emit(CHANGE_EVENT, value)
}
const getValue = (item: Option) => {
return isObject(item) ? item.value : item
}
const getLabel = (item: Option) => {
return isObject(item) ? item.label : item
}
const getDisabled = (item: Option) => {
return !!(_disabled.value || (isObject(item) ? item.disabled : false))
}
const getSelected = (item: Option) => {
return props.modelValue === getValue(item)
}
const getOption = (value: any) => {
return props.options.find((item) => getValue(item) === value)
}
const getItemCls = (item: Option) => {
return [
ns.e('item'),
ns.is('selected', getSelected(item)),
ns.is('disabled', getDisabled(item)),
]
}
const updateSelect = () => {
if (!segmentedRef.value) return
const selectedItem = segmentedRef.value.querySelector(
'.is-selected'
) as HTMLElement
const selectedItemInput = segmentedRef.value.querySelector(
'.is-selected input'
) as HTMLElement
if (!selectedItem || !selectedItemInput) {
state.width = 0
state.translateX = 0
state.disabled = false
state.focusVisible = false
return
}
const rect = selectedItem.getBoundingClientRect()
state.isInit = true
state.width = rect.width
state.translateX = selectedItem.offsetLeft
state.disabled = getDisabled(getOption(props.modelValue))
try {
// This will failed in test
state.focusVisible = selectedItemInput.matches(':focus-visible')
} catch {}
}
const segmentedCls = computed(() => [
ns.b(),
ns.m(segmentedSize.value),
ns.is('block', props.block),
])
const selectedStyle = computed(() => ({
width: `${state.width}px`,
transform: `translateX(${state.translateX}px)`,
display: state.isInit ? 'block' : 'none',
}))
const selectedCls = computed(() => [
ns.e('item-selected'),
ns.is('disabled', state.disabled),
ns.is('focus-visible', state.focusVisible),
])
const name = computed(() => {
return props.name || segmentedId.value
})
useResizeObserver(segmentedRef, updateSelect)
watch(activeElement, updateSelect)
watch(
() => props.modelValue,
() => {
updateSelect()
if (props.validateEvent) {
formItem?.validate?.('change').catch((err) => debugWarn(err))
}
},
{
flush: 'post',
}
)
</script>

View File

@ -0,0 +1,11 @@
export type Option =
| {
label: string
value: string | number | boolean
disabled?: boolean
[key: string]: any
}
| string
| number
| boolean
| undefined

View File

@ -0,0 +1,2 @@
import '@element-plus/components/base/style/css'
import '@element-plus/theme-chalk/el-segmented.css'

View File

@ -0,0 +1,2 @@
import '@element-plus/components/base/style'
import '@element-plus/theme-chalk/src/segmented.scss'

View File

@ -105,6 +105,7 @@ import { ElUpload } from '@element-plus/components/upload'
import { ElWatermark } from '@element-plus/components/watermark'
import { ElTour, ElTourStep } from '@element-plus/components/tour'
import { ElAnchor, ElAnchorLink } from '@element-plus/components/anchor'
import { ElSegmented } from '@element-plus/components/segmented'
import type { Plugin } from 'vue'
@ -212,4 +213,5 @@ export default [
ElTourStep,
ElAnchor,
ElAnchorLink,
ElSegmented,
] as Plugin[]

View File

@ -832,6 +832,25 @@ $anchor: map.merge(
$anchor
);
// Segmented
// css3 var in packages/theme-chalk/src/segmented.scss
$segmented: () !default;
$segmented: map.merge(
(
'color': getCssVar('text-color', 'regular'),
'bg-color': getCssVar('fill-color', 'light'),
'padding': 2px,
'item-selected-color': getCssVar('color-white'),
'item-selected-bg-color': getCssVar('color-primary'),
'item-selected-disabled-bg-color': getCssVar('color-primary', 'light-5'),
'item-hover-color': getCssVar('text-color', 'primary'),
'item-hover-bg-color': getCssVar('fill-color', 'dark'),
'item-active-bg-color': getCssVar('fill-color', 'darker'),
'item-disabled-color': getCssVar('text-color', 'placeholder'),
),
$segmented
);
// Table
// css3 var in packages/theme-chalk/src/table.scss
$table: () !default;

View File

@ -106,3 +106,4 @@
@use './tour.scss';
@use './anchor.scss';
@use './anchor-link.scss';
@use './segmented.scss';

View File

@ -0,0 +1,160 @@
@use 'sass:map';
@use 'mixins/mixins' as *;
@use 'mixins/utils' as *;
@use 'mixins/var' as *;
@use 'common/var' as *;
$segmented-border-radius: () !default;
$segmented-border-radius: map.merge(
(
'large': map.get($button-border-radius, 'large'),
'default': map.get($button-border-radius, 'default'),
'small': map.get($button-border-radius, 'small'),
),
$segmented-border-radius
);
$segmented-font-size: () !default;
$segmented-font-size: map.merge(
(
'large': 16px,
'default': 14px,
'small': 14px,
),
$segmented-font-size
);
$segmented-item-padding: () !default;
$segmented-item-padding: map.merge(
(
'large': 0 11px,
'default':0 11px,
'small': 0 7px,
),
$segmented-item-padding
);
@include b(segmented) {
@include set-component-css-var('segmented', $segmented);
}
@include b(segmented) {
display: inline-flex;
align-items: stretch;
min-height: map.get($input-height, 'default');
background: getCssVar('segmented-bg-color');
padding: getCssVar('segmented-padding');
border-radius: map.get($segmented-border-radius, 'default');
font-size: map.get($segmented-font-size, 'default');
color: getCssVar('segmented-color');
box-sizing: border-box;
@include e(group) {
display: flex;
align-items: stretch;
position: relative;
width: 100%;
}
@include e(item-selected) {
position: absolute;
top: 0;
left: 0;
background: getCssVar('segmented-item-selected-bg-color');
height: 100%;
width: 10px;
border-radius: calc(#{map.get($segmented-border-radius, 'default')} - 2px);
transition: all 0.3s;
pointer-events: none;
@include when(disabled) {
background: getCssVar('segmented-item-selected-disabled-bg-color');
}
@include when(focus-visible) {
&:before {
position: absolute;
content: '';
inset: 0;
border-radius: inherit;
outline: 2px solid getCssVar('segmented-item-selected-bg-color');
outline-offset: 1px;
}
}
}
@include e(item) {
display: flex;
align-items: center;
flex: 1;
cursor: pointer;
border-radius: calc(#{map.get($segmented-border-radius, 'default')} - 2px);
padding: map.get($segmented-item-padding, 'default');
&:not(.is-disabled):not(.is-selected):hover {
color: getCssVar('segmented-item-hover-color');
background: getCssVar('segmented-item-hover-bg-color');
}
&:not(.is-disabled):not(.is-selected):active {
background: getCssVar('segmented-item-active-bg-color');
}
@include when(selected) {
color: getCssVar('segmented-item-selected-color');
&.is-disabled {
color: getCssVar('segmented-item-selected-color');
}
}
@include when(disabled) {
cursor: not-allowed;
color: getCssVar('segmented-item-disabled-color');
}
}
@include e(item-input) {
position: absolute;
margin: 0;
width: 0;
height: 0;
opacity: 0;
pointer-events: none;
}
@include e(item-label) {
flex: 1;
text-align: center;
line-height: normal;
@include utils-ellipsis;
transition: color 0.3s;
z-index: 1;
}
@include when(block) {
display: flex;
.#{$namespace}-segmented__item {
min-width: 0;
}
}
@each $size in (large, small) {
@include m($size) {
min-height: map.get($input-height, $size);
border-radius: map.get($segmented-border-radius, $size);
font-size: map.get($segmented-font-size, $size);
@include e(item-selected) {
border-radius: calc(#{map.get($segmented-border-radius, $size)} - 2px);
}
@include e(item) {
border-radius: calc(#{map.get($segmented-border-radius, $size)} - 2px);
padding: map.get($segmented-item-padding, $size);
}
}
}
}

View File

@ -103,6 +103,7 @@ declare module '@vue/runtime-core' {
ElTourStep: typeof import('../packages/element-plus')['ElTourStep']
ElAnchor: typeof import('../packages/element-plus')['ElAnchor']
ElAnchorLink: typeof import('../packages/element-plus')['ElAnchorLink']
ElSegmented: typeof import('../packages/element-plus')['ElSegmented']
}
interface ComponentCustomProperties {