refactor: segmented #6286
parent
62e7f94aba
commit
9df8317ece
|
@ -59,8 +59,18 @@ export function functionType<T = () => {}>(defaultVal?: T) {
|
||||||
return { type: Function as PropType<T>, default: defaultVal as T };
|
return { type: Function as PropType<T>, default: defaultVal as T };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function anyType<T = any>(defaultVal?: T) {
|
export function anyType<T = any>(defaultVal?: T, required?: boolean) {
|
||||||
return { validator: () => true, default: defaultVal as T } as unknown as { type: PropType<T> };
|
const type = { validator: () => true, default: defaultVal as T } as unknown;
|
||||||
|
return required
|
||||||
|
? (type as {
|
||||||
|
type: PropType<T>;
|
||||||
|
default: T;
|
||||||
|
required: true;
|
||||||
|
})
|
||||||
|
: (type as {
|
||||||
|
default: T;
|
||||||
|
type: PropType<T>;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
export function vNodeType<T = VueNode>() {
|
export function vNodeType<T = VueNode>() {
|
||||||
return { validator: () => true } as unknown as { type: PropType<T> };
|
return { validator: () => true } as unknown as { type: PropType<T> };
|
||||||
|
|
|
@ -15,17 +15,19 @@ The most basic usage.
|
||||||
</docs>
|
</docs>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<a-segmented :options="data" />
|
<a-segmented v-model:value="value" :options="data" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, reactive } from 'vue';
|
import { defineComponent, reactive, ref } from 'vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
setup() {
|
setup() {
|
||||||
const data = reactive(['Daily', 'Weekly', 'Monthly', 'Quarterly', 'Yearly']);
|
const data = reactive(['Daily', 'Weekly', 'Monthly', 'Quarterly', 'Yearly']);
|
||||||
|
const value = ref(data[0]);
|
||||||
return {
|
return {
|
||||||
data,
|
data,
|
||||||
|
value,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,17 +14,19 @@ title:
|
||||||
`block` property will make the `Segmented` fit to its parent width.
|
`block` property will make the `Segmented` fit to its parent width.
|
||||||
</docs>
|
</docs>
|
||||||
<template>
|
<template>
|
||||||
<a-segmented block :options="data" />
|
<a-segmented v-model:value="value" block :options="data" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, reactive } from 'vue';
|
import { defineComponent, reactive, ref } from 'vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
setup() {
|
setup() {
|
||||||
const data = reactive([123, 456, 'longtext-longtext-longtext-longtext']);
|
const data = reactive([123, 456, 'longtext-longtext-longtext-longtext']);
|
||||||
|
const value = ref(data[0]);
|
||||||
return {
|
return {
|
||||||
data,
|
data,
|
||||||
|
value,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
<docs>
|
|
||||||
---
|
|
||||||
order: 4
|
|
||||||
title:
|
|
||||||
zh-CN: 受控模式
|
|
||||||
en-US: Controlled mode
|
|
||||||
---
|
|
||||||
|
|
||||||
## zh-CN
|
|
||||||
|
|
||||||
受控的 Segmented
|
|
||||||
|
|
||||||
## en-US
|
|
||||||
Controlled Segmented.
|
|
||||||
</docs>
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<a-segmented :options="data" default-value="1" @change="handle" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent, reactive } from 'vue';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
setup() {
|
|
||||||
const data = reactive(['Map', 'Transit', 'Satellite']);
|
|
||||||
const handle = v => console.log(v);
|
|
||||||
return {
|
|
||||||
data,
|
|
||||||
handle,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
|
|
@ -14,80 +14,103 @@ title:
|
||||||
Custom each Segmented Item.
|
Custom each Segmented Item.
|
||||||
</docs>
|
</docs>
|
||||||
<template>
|
<template>
|
||||||
<a-segmented :options="data">
|
<a-segmented v-model:value="value" :options="data">
|
||||||
<template #title="index">
|
<template #label="{ value: val, payload = {} }">
|
||||||
<template v-if="index === 0">
|
|
||||||
<div style="padding: 4px 4px">
|
<div style="padding: 4px 4px">
|
||||||
<a-avatar src="https://joeschmoe.io/api/v1/random" />
|
<template v-if="payload.icon">
|
||||||
<div>User 1</div>
|
<a-avatar :src="payload.src" :style="payload.style">
|
||||||
</div>
|
<template #icon><component :is="payload.icon" /></template>
|
||||||
</template>
|
{{ payload.content }}
|
||||||
<template v-if="index === 1">
|
|
||||||
<div style="padding: 4px 4px">
|
|
||||||
<a-avatar style="background-color: #f56a00">K</a-avatar>
|
|
||||||
<div>User 2</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template v-if="index === 2">
|
|
||||||
<div style="padding: 4px 4px">
|
|
||||||
<a-avatar style="background-color: #1890ff">
|
|
||||||
<template #icon><UserOutlined /></template>
|
|
||||||
</a-avatar>
|
</a-avatar>
|
||||||
<div>User 3</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<a-avatar :src="payload.src" :style="payload.style">
|
||||||
|
{{ payload.content }}
|
||||||
|
</a-avatar>
|
||||||
|
</template>
|
||||||
|
<div>{{ val }}</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</a-segmented>
|
</a-segmented>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<a-segmented :options="options2">
|
<a-segmented v-model:value="value2" :options="options2">
|
||||||
<template #title="index">
|
<template #label="{ payload }">
|
||||||
<template v-if="index === 0">
|
|
||||||
<div style="padding: 4px 4px">
|
<div style="padding: 4px 4px">
|
||||||
<div>Spring</div>
|
<div>{{ payload.title }}</div>
|
||||||
<div>Jan-Mar</div>
|
<div>{{ payload.subTitle }}</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="index === 1">
|
|
||||||
<div style="padding: 4px 4px">
|
|
||||||
<div>Summer</div>
|
|
||||||
<div>Apr-Jun</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template v-if="index === 2">
|
|
||||||
<div style="padding: 4px 4px">
|
|
||||||
<div>Autumn</div>
|
|
||||||
<div>Jul-Sept</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template v-if="index === 3">
|
|
||||||
<div style="padding: 4px 4px">
|
|
||||||
<div>Winter</div>
|
|
||||||
<div>Oct-Dec</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</a-segmented>
|
</a-segmented>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, reactive } from 'vue';
|
import { defineComponent, ref } from 'vue';
|
||||||
import { UserOutlined } from '@ant-design/icons-vue';
|
import { UserOutlined } from '@ant-design/icons-vue';
|
||||||
import ASegmented from 'ant-design-vue/es/segmented/src/segmented';
|
import ASegmented from 'ant-design-vue/es/segmented/src/segmented';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { ASegmented, UserOutlined },
|
components: { ASegmented, UserOutlined },
|
||||||
setup() {
|
setup() {
|
||||||
const data = reactive([{ value: 'user1' }, { value: 'user2' }, { value: 'user3' }]);
|
const data = ref([
|
||||||
const options2 = reactive([
|
{
|
||||||
{ value: 'spring' },
|
value: 'user1',
|
||||||
{ value: 'summer' },
|
payload: {
|
||||||
{ value: 'autumn' },
|
src: 'https://joeschmoe.io/api/v1/random',
|
||||||
{ value: 'winter' },
|
style: { backgroundColor: '#f56a00' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'user2',
|
||||||
|
payload: {
|
||||||
|
style: { backgroundColor: '#f56a00' },
|
||||||
|
content: 'K',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'user3',
|
||||||
|
payload: {
|
||||||
|
icon: UserOutlined,
|
||||||
|
style: { backgroundColor: '#f56a00' },
|
||||||
|
},
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
|
const options2 = ref([
|
||||||
|
{
|
||||||
|
value: 'spring',
|
||||||
|
payload: {
|
||||||
|
title: 'Spring',
|
||||||
|
subTitle: 'Jan-Mar',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'summer',
|
||||||
|
payload: {
|
||||||
|
title: 'Summer',
|
||||||
|
subTitle: 'Apr-Jun',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'autumn',
|
||||||
|
payload: {
|
||||||
|
title: 'Autumn',
|
||||||
|
subTitle: 'Jul-Sept',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'winter',
|
||||||
|
payload: {
|
||||||
|
title: 'Winter',
|
||||||
|
subTitle: 'Oct-Dec',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const value = ref('user1');
|
||||||
|
const value2 = ref('spring');
|
||||||
return {
|
return {
|
||||||
data,
|
data,
|
||||||
options2,
|
options2,
|
||||||
|
value,
|
||||||
|
value2,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,15 +15,15 @@ Disabled Segmented.
|
||||||
</docs>
|
</docs>
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<a-segmented disabled :options="data" />
|
<a-segmented v-model:value="value" disabled :options="data" />
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<a-segmented :options="data2" />
|
<a-segmented v-model:value="value2" :options="data2" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, reactive } from 'vue';
|
import { defineComponent, reactive, ref } from 'vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
setup() {
|
setup() {
|
||||||
|
@ -35,9 +35,13 @@ export default defineComponent({
|
||||||
{ value: 'Quarterly', disabled: true },
|
{ value: 'Quarterly', disabled: true },
|
||||||
'Yearly',
|
'Yearly',
|
||||||
]);
|
]);
|
||||||
|
const value = ref(data[0]);
|
||||||
|
const value2 = ref('Daily');
|
||||||
return {
|
return {
|
||||||
data,
|
data,
|
||||||
data2,
|
data2,
|
||||||
|
value,
|
||||||
|
value2,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,10 +14,10 @@ title:
|
||||||
Load dynamically.
|
Load dynamically.
|
||||||
</docs>
|
</docs>
|
||||||
<template>
|
<template>
|
||||||
<a-segmented :options="data"></a-segmented>
|
<a-segmented v-model:value="value" :options="data"></a-segmented>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<a-button type="primary" @click="loadMore" :disabled="isDisabled">Load More</a-button>
|
<a-button type="primary" :disabled="isDisabled" @click="loadMore">Load More</a-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
@ -25,15 +25,17 @@ import { defineComponent, reactive, ref } from 'vue';
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
setup() {
|
setup() {
|
||||||
const data = reactive(['Daily', 'Weekly', 'Monthly']);
|
const data = reactive(['Daily', 'Weekly', 'Monthly']);
|
||||||
const isDisabled = ref<boolean>(false);
|
const isDisabled = ref(false);
|
||||||
const loadMore = () => {
|
const loadMore = () => {
|
||||||
data.push(...['Quarterly', 'Yearly']);
|
data.push(...['Quarterly', 'Yearly']);
|
||||||
isDisabled.value = true;
|
isDisabled.value = true;
|
||||||
};
|
};
|
||||||
|
const value = ref(data[0]);
|
||||||
return {
|
return {
|
||||||
data,
|
data,
|
||||||
loadMore,
|
loadMore,
|
||||||
isDisabled,
|
isDisabled,
|
||||||
|
value,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
<docs>
|
|
||||||
---
|
|
||||||
order: 7
|
|
||||||
title:
|
|
||||||
zh-CN: 设置图标
|
|
||||||
en-US: With Icon
|
|
||||||
---
|
|
||||||
|
|
||||||
## zh-CN
|
|
||||||
|
|
||||||
给 Segmented Item 设置 Icon。
|
|
||||||
|
|
||||||
## en-US
|
|
||||||
Set `icon` for Segmented Item.
|
|
||||||
</docs>
|
|
||||||
<template>
|
|
||||||
<a-segmented :options="data">
|
|
||||||
<template #icon="index">
|
|
||||||
<template v-if="index == 0">
|
|
||||||
<unordered-list-outlined />
|
|
||||||
</template>
|
|
||||||
<template v-if="index == 1">
|
|
||||||
<appstore-outlined />
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</a-segmented>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent, reactive } from 'vue';
|
|
||||||
import { UnorderedListOutlined, AppstoreOutlined } from '@ant-design/icons-vue';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
components: { UnorderedListOutlined, AppstoreOutlined },
|
|
||||||
setup() {
|
|
||||||
const data = reactive([
|
|
||||||
{
|
|
||||||
value: 'List',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'Kanban',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
return {
|
|
||||||
data,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
|
|
@ -3,11 +3,9 @@
|
||||||
<basic />
|
<basic />
|
||||||
<block />
|
<block />
|
||||||
<disabled />
|
<disabled />
|
||||||
<controlled />
|
|
||||||
<custom />
|
<custom />
|
||||||
<dynamic />
|
<dynamic />
|
||||||
<size />
|
<size />
|
||||||
<icon />
|
|
||||||
</demo-sort>
|
</demo-sort>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -18,14 +16,12 @@ import US from '../index.en-US.md';
|
||||||
import Basic from './basic.vue';
|
import Basic from './basic.vue';
|
||||||
import Block from './block.vue';
|
import Block from './block.vue';
|
||||||
import Disabled from './disabled.vue';
|
import Disabled from './disabled.vue';
|
||||||
import Controlled from './controlled.vue';
|
|
||||||
import Custom from './custom.vue';
|
import Custom from './custom.vue';
|
||||||
import Dynamic from './dynamic.vue';
|
import Dynamic from './dynamic.vue';
|
||||||
import Size from './size.vue';
|
import Size from './size.vue';
|
||||||
import Icon from './icon.vue';
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { Icon, Size, Dynamic, Custom, Controlled, Disabled, Block, Basic },
|
components: { Size, Dynamic, Custom, Disabled, Block, Basic },
|
||||||
category: 'Components',
|
category: 'Components',
|
||||||
subtitle: '分段控制器',
|
subtitle: '分段控制器',
|
||||||
type: 'Data Display',
|
type: 'Data Display',
|
||||||
|
|
|
@ -14,23 +14,29 @@ title:
|
||||||
There are three sizes of an a-segmented: `large` (40px), `default` (32px) and `small` (24px).
|
There are three sizes of an a-segmented: `large` (40px), `default` (32px) and `small` (24px).
|
||||||
</docs>
|
</docs>
|
||||||
<template>
|
<template>
|
||||||
<a-segmented :options="data" size="large" />
|
<a-segmented v-model:value="value" :options="data" size="large" />
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<a-segmented :options="data" />
|
<a-segmented v-model:value="value2" :options="data" />
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<a-segmented :options="data" size="small" />
|
<a-segmented v-model:value="value3" :options="data" size="small" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, reactive } from 'vue';
|
import { defineComponent, ref, reactive } from 'vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
setup() {
|
setup() {
|
||||||
const data = reactive(['Daily', 'Weekly', 'Monthly', 'Quarterly', 'Yearly']);
|
const data = reactive(['Daily', 'Weekly', 'Monthly', 'Quarterly', 'Yearly']);
|
||||||
|
const value = ref(data[0]);
|
||||||
|
const value2 = ref(data[0]);
|
||||||
|
const value3 = ref(data[0]);
|
||||||
return {
|
return {
|
||||||
data,
|
data,
|
||||||
|
value,
|
||||||
|
value2,
|
||||||
|
value3,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -13,14 +13,35 @@ Segmented Controls.
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
### Segmented
|
|
||||||
|
|
||||||
| Property | Description | Type | Default | Version |
|
| Property | Description | Type | Default | Version |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| block | Option to fit width to its parent\'s width | boolean | false | |
|
| block | Option to fit width to its parent\'s width | boolean | false | |
|
||||||
| defaultValue | Default selected value | string \| number | | |
|
|
||||||
| disabled | Disable all segments | boolean | false | |
|
| disabled | Disable all segments | boolean | false | |
|
||||||
| change | The callback function that is triggered when the state changes | function(value: string \| number) | | |
|
| options | Set children optional | string[] \| number[] \| SegmentedOption[] | [] | |
|
||||||
| options | Set children optional | string[] \| number[] \| Array<{ value?: string disabled?: boolean }> | [] | |
|
|
||||||
| size | The size of the Segmented. | `large` \| `middle` \| `small` | - | |
|
| size | The size of the Segmented. | `large` \| `middle` \| `small` | - | |
|
||||||
| value | Currently selected value | string \| number | | |
|
| value | Currently selected value | string \| number | | |
|
||||||
|
| label | custom label by slot | v-slot:label="SegmentedBaseOption" | | |
|
||||||
|
|
||||||
|
### events
|
||||||
|
|
||||||
|
| Events Name | Description | Arguments | |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| change | The callback function that is triggered when the state changes | function(value: string \| number) | - |
|
||||||
|
|
||||||
|
#### SegmentedBaseOption、 SegmentedOption
|
||||||
|
|
||||||
|
```ts
|
||||||
|
interface SegmentedBaseOption {
|
||||||
|
value: string | number;
|
||||||
|
disabled?: boolean;
|
||||||
|
payload?: any; // payload more data
|
||||||
|
/**
|
||||||
|
* html `title` property for label
|
||||||
|
*/
|
||||||
|
title?: string;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
interface SegmentedOption extends SegmentedBaseOption {
|
||||||
|
label?: VueNode | ((option: SegmentedBaseOption) => VueNode);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
import type { App } from 'vue';
|
|
||||||
import Segmented from './src';
|
import Segmented from './src';
|
||||||
import type { SegmentedProps } from './src';
|
import type { SegmentedProps } from './src';
|
||||||
|
import { withInstall } from '../_util/type';
|
||||||
|
|
||||||
Segmented.install = function (app: App) {
|
export default withInstall(Segmented);
|
||||||
app.component(Segmented.name, Segmented);
|
|
||||||
return app;
|
|
||||||
};
|
|
||||||
export default Segmented;
|
|
||||||
export type { SegmentedProps };
|
export type { SegmentedProps };
|
||||||
|
|
|
@ -19,9 +19,32 @@ title: Segmented
|
||||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| block | 将宽度调整为父元素宽度的选项 | boolean | 无 | |
|
| block | 将宽度调整为父元素宽度的选项 | boolean | 无 | |
|
||||||
| defaultValue | 默认选中的值 | string \| number | | |
|
|
||||||
| disabled | 是否禁用 | boolean | false | |
|
| disabled | 是否禁用 | boolean | false | |
|
||||||
| change | 选项变化时的回调函数 | function(value: string \| number) | | |
|
| options | 数据化配置选项内容 | string[] \| number[] \| SegmentedOption[] | [] | |
|
||||||
| options | 数据化配置选项内容 | string[] \| number[] \| Array<{ value?: string disabled?: boolean }> | [] | |
|
|
||||||
| size | 控件尺寸 | `large` \| `middle` \| `small` | - | |
|
| size | 控件尺寸 | `large` \| `middle` \| `small` | - | |
|
||||||
| value | 当前选中的值 | string \| number | | |
|
| value | 当前选中的值 | string \| number | | |
|
||||||
|
| label | 使用插槽自定义 label | v-slot:label="SegmentedBaseOption" | | |
|
||||||
|
|
||||||
|
### 事件
|
||||||
|
|
||||||
|
| 事件名称 | 说明 | 回调参数 | |
|
||||||
|
| -------- | -------------------- | --------------------------------- | --- |
|
||||||
|
| change | 选项变化时的回调函数 | function(value: string \| number) | - |
|
||||||
|
|
||||||
|
#### SegmentedBaseOption、SegmentedOption
|
||||||
|
|
||||||
|
```ts
|
||||||
|
interface SegmentedBaseOption {
|
||||||
|
value: string | number;
|
||||||
|
disabled?: boolean;
|
||||||
|
payload?: any; // payload more data
|
||||||
|
/**
|
||||||
|
* html `title` property for label
|
||||||
|
*/
|
||||||
|
title?: string;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
interface SegmentedOption extends SegmentedBaseOption {
|
||||||
|
label?: VueNode | ((option: SegmentedBaseOption) => VueNode);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
|
@ -0,0 +1,161 @@
|
||||||
|
import { addClass, removeClass } from 'ant-design-vue/es/vc-util/Dom/class';
|
||||||
|
import type { CSSProperties, Ref, TransitionProps } from 'vue';
|
||||||
|
import { onBeforeUnmount, nextTick, Transition, watch, defineComponent, computed, ref } from 'vue';
|
||||||
|
import { anyType } from '../../_util/type';
|
||||||
|
import type { SegmentedValue } from './segmented';
|
||||||
|
|
||||||
|
type ThumbReact = {
|
||||||
|
left: number;
|
||||||
|
right: number;
|
||||||
|
width: number;
|
||||||
|
} | null;
|
||||||
|
|
||||||
|
export interface MotionThumbInterface {
|
||||||
|
value: SegmentedValue;
|
||||||
|
getValueIndex: (value: SegmentedValue) => number;
|
||||||
|
prefixCls: string;
|
||||||
|
motionName: string;
|
||||||
|
onMotionStart: VoidFunction;
|
||||||
|
onMotionEnd: VoidFunction;
|
||||||
|
direction?: 'ltr' | 'rtl';
|
||||||
|
}
|
||||||
|
|
||||||
|
const calcThumbStyle = (targetElement: HTMLElement | null | undefined): ThumbReact =>
|
||||||
|
targetElement
|
||||||
|
? {
|
||||||
|
left: targetElement.offsetLeft,
|
||||||
|
right:
|
||||||
|
(targetElement.parentElement!.clientWidth as number) -
|
||||||
|
targetElement.clientWidth -
|
||||||
|
targetElement.offsetLeft,
|
||||||
|
width: targetElement.clientWidth,
|
||||||
|
}
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const toPX = (value?: number) => (value !== undefined ? `${value}px` : undefined);
|
||||||
|
|
||||||
|
const MotionThumb = defineComponent({
|
||||||
|
props: {
|
||||||
|
value: anyType<SegmentedValue>(),
|
||||||
|
getValueIndex: anyType<(value: SegmentedValue) => number>(),
|
||||||
|
prefixCls: anyType<string>(),
|
||||||
|
motionName: anyType<string>(),
|
||||||
|
onMotionStart: anyType<VoidFunction>(),
|
||||||
|
onMotionEnd: anyType<VoidFunction>(),
|
||||||
|
direction: anyType<'ltr' | 'rtl'>(),
|
||||||
|
containerRef: anyType<Ref<HTMLDivElement>>(),
|
||||||
|
},
|
||||||
|
emits: ['motionStart', 'motionEnd'],
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const thumbRef = ref<HTMLDivElement>();
|
||||||
|
// =========================== Effect ===========================
|
||||||
|
const findValueElement = (val: SegmentedValue) => {
|
||||||
|
const index = props.getValueIndex(val);
|
||||||
|
|
||||||
|
const ele = props.containerRef.value?.querySelectorAll<HTMLDivElement>(
|
||||||
|
`.${props.prefixCls}-item`,
|
||||||
|
)[index];
|
||||||
|
return ele?.offsetParent && ele;
|
||||||
|
};
|
||||||
|
|
||||||
|
const prevStyle = ref<ThumbReact>(null);
|
||||||
|
const nextStyle = ref<ThumbReact>(null);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.value,
|
||||||
|
(value, prevValue) => {
|
||||||
|
const prev = findValueElement(prevValue);
|
||||||
|
const next = findValueElement(value);
|
||||||
|
|
||||||
|
const calcPrevStyle = calcThumbStyle(prev);
|
||||||
|
const calcNextStyle = calcThumbStyle(next);
|
||||||
|
|
||||||
|
prevStyle.value = calcPrevStyle;
|
||||||
|
nextStyle.value = calcNextStyle;
|
||||||
|
|
||||||
|
if (prev && next) {
|
||||||
|
emit('motionStart');
|
||||||
|
} else {
|
||||||
|
emit('motionEnd');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ flush: 'post' },
|
||||||
|
);
|
||||||
|
|
||||||
|
const thumbStart = computed(() =>
|
||||||
|
props.direction === 'rtl'
|
||||||
|
? toPX(-(prevStyle.value?.right as number))
|
||||||
|
: toPX(prevStyle.value?.left as number),
|
||||||
|
);
|
||||||
|
const thumbActive = computed(() =>
|
||||||
|
props.direction === 'rtl'
|
||||||
|
? toPX(-(nextStyle.value?.right as number))
|
||||||
|
: toPX(nextStyle.value?.left as number),
|
||||||
|
);
|
||||||
|
|
||||||
|
// =========================== Motion ===========================
|
||||||
|
let timeid: any;
|
||||||
|
const onAppearStart: TransitionProps['onBeforeEnter'] = (el: HTMLDivElement) => {
|
||||||
|
clearTimeout(timeid);
|
||||||
|
nextTick(() => {
|
||||||
|
if (el) {
|
||||||
|
el.style.transform = `translateX(var(--thumb-start-left))`;
|
||||||
|
el.style.width = `var(--thumb-start-width)`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onAppearActive: TransitionProps['onEnter'] = (el: HTMLDivElement) => {
|
||||||
|
timeid = setTimeout(() => {
|
||||||
|
if (el) {
|
||||||
|
addClass(el, `${props.motionName}-appear-active`);
|
||||||
|
el.style.transform = `translateX(var(--thumb-active-left))`;
|
||||||
|
el.style.width = `var(--thumb-active-width)`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const onAppearEnd: TransitionProps['onAfterEnter'] = (el: HTMLDivElement) => {
|
||||||
|
prevStyle.value = null;
|
||||||
|
nextStyle.value = null;
|
||||||
|
if (el) {
|
||||||
|
el.style.transform = null;
|
||||||
|
el.style.width = null;
|
||||||
|
removeClass(el, `${props.motionName}-appear-active`);
|
||||||
|
}
|
||||||
|
emit('motionEnd');
|
||||||
|
};
|
||||||
|
const mergedStyle = computed<CSSProperties>(() => ({
|
||||||
|
'--thumb-start-left': thumbStart.value,
|
||||||
|
'--thumb-start-width': toPX(prevStyle.value?.width),
|
||||||
|
'--thumb-active-left': thumbActive.value,
|
||||||
|
'--thumb-active-width': toPX(nextStyle.value?.width),
|
||||||
|
}));
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
clearTimeout(timeid);
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
// It's little ugly which should be refactor when @umi/test update to latest jsdom
|
||||||
|
const motionProps = {
|
||||||
|
ref: thumbRef,
|
||||||
|
style: mergedStyle.value,
|
||||||
|
class: [`${props.prefixCls}-thumb`],
|
||||||
|
};
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === 'test') {
|
||||||
|
(motionProps as any)['data-test-style'] = JSON.stringify(mergedStyle.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Transition
|
||||||
|
appear
|
||||||
|
onBeforeEnter={onAppearStart}
|
||||||
|
onEnter={onAppearActive}
|
||||||
|
onAfterEnter={onAppearEnd}
|
||||||
|
>
|
||||||
|
{!prevStyle.value || !nextStyle.value ? null : <div {...motionProps}></div>}
|
||||||
|
</Transition>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
export default MotionThumb;
|
|
@ -1,151 +1,170 @@
|
||||||
import { defineComponent, ref, toRefs, reactive, watch } from 'vue';
|
import { defineComponent, ref, computed } from 'vue';
|
||||||
import type { ExtractPropTypes, PropType } from 'vue';
|
import type { ExtractPropTypes, FunctionalComponent } from 'vue';
|
||||||
import classNames from '../../_util/classNames';
|
import classNames from '../../_util/classNames';
|
||||||
import useConfigInject from '../../config-provider/hooks/useConfigInject';
|
import useConfigInject from '../../config-provider/hooks/useConfigInject';
|
||||||
import { getPropsSlot, initDefaultProps } from '../../_util/props-util';
|
import { initDefaultProps } from '../../_util/props-util';
|
||||||
import useStyle from '../style';
|
import useStyle from '../style';
|
||||||
|
import type { VueNode } from '../../_util/type';
|
||||||
|
import { someType, arrayType, booleanType, stringType } from '../../_util/type';
|
||||||
|
import type { ChangeEvent } from '../../_util/EventInterface';
|
||||||
|
import MotionThumb from './MotionThumb';
|
||||||
|
export type SegmentedValue = string | number;
|
||||||
export type segmentedSize = 'large' | 'small';
|
export type segmentedSize = 'large' | 'small';
|
||||||
export interface SegmentedOptions {
|
export interface SegmentedBaseOption {
|
||||||
value?: string;
|
value: string | number;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
payload?: any;
|
||||||
|
/**
|
||||||
|
* html `title` property for label
|
||||||
|
*/
|
||||||
|
title?: string;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
export interface SegmentedOption extends SegmentedBaseOption {
|
||||||
|
label?: VueNode | ((option: SegmentedBaseOption) => VueNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeOptions(options: (SegmentedOption | string | number)[]) {
|
||||||
|
return options.map(option => {
|
||||||
|
if (typeof option === 'object' && option !== null) {
|
||||||
|
return option;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
label: option?.toString(),
|
||||||
|
title: option?.toString(),
|
||||||
|
value: option as unknown as SegmentedBaseOption['value'],
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
export const segmentedProps = () => {
|
export const segmentedProps = () => {
|
||||||
return {
|
return {
|
||||||
options: { type: Array as PropType<Array<SegmentedOptions | string | number>> },
|
prefixCls: String,
|
||||||
defaultValue: { type: [Number, String] },
|
options: arrayType<(SegmentedOption | string | number)[]>(),
|
||||||
block: Boolean,
|
block: booleanType(),
|
||||||
disabled: Boolean,
|
disabled: booleanType(),
|
||||||
size: { type: String as PropType<segmentedSize> },
|
size: stringType<segmentedSize>(),
|
||||||
|
value: { ...someType<SegmentedValue>([String, Number]), required: true },
|
||||||
|
motionName: String,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
export type SegmentedProps = Partial<ExtractPropTypes<ReturnType<typeof segmentedProps>>>;
|
export type SegmentedProps = Partial<ExtractPropTypes<ReturnType<typeof segmentedProps>>>;
|
||||||
export default defineComponent({
|
|
||||||
name: 'ASegmented',
|
const SegmentedOption: FunctionalComponent<
|
||||||
inheritAttrs: false,
|
SegmentedOption & { prefixCls: string; checked: boolean }
|
||||||
props: { ...initDefaultProps(segmentedProps(), {}) },
|
> = (props, { slots, emit, attrs }) => {
|
||||||
emits: ['change', 'value'],
|
const { value, disabled, payload, title, prefixCls, label = slots.label, checked } = props;
|
||||||
slots: ['icon', 'title'],
|
const handleChange = (event: InputEvent) => {
|
||||||
setup(props, { emit, slots }) {
|
if (disabled) {
|
||||||
const { prefixCls } = useConfigInject('segmented', props);
|
return;
|
||||||
const [wrapSSR, hashId] = useStyle(prefixCls);
|
|
||||||
const pre = prefixCls.value;
|
|
||||||
const { size } = toRefs(props);
|
|
||||||
const itemRef = ref([]);
|
|
||||||
const { options, disabled, defaultValue } = toRefs(props);
|
|
||||||
const segmentedItemInput = () => {
|
|
||||||
return <input type="radio" class={`${pre}-item-input`} disabled checked />;
|
|
||||||
};
|
|
||||||
const isDisabled = item => {
|
|
||||||
if (disabled.value || (typeof item == 'object' && item.disabled)) {
|
|
||||||
return segmentedItemInput();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emit('change', event, value);
|
||||||
};
|
};
|
||||||
const currentItemKey = ref();
|
|
||||||
currentItemKey.value = defaultValue.value ? defaultValue.value : 0;
|
|
||||||
const toPX = (value: number) => (value !== undefined ? `${value}px` : undefined);
|
|
||||||
// 开始 or 停止
|
|
||||||
const thumbShow = ref(true);
|
|
||||||
const mergedStyle = reactive({
|
|
||||||
startLeft: '',
|
|
||||||
startWidth: '',
|
|
||||||
activeLeft: '',
|
|
||||||
activeWidth: '',
|
|
||||||
});
|
|
||||||
const handleSelectedChange = (item, index) => {
|
|
||||||
if (disabled.value || item.disabled) return;
|
|
||||||
currentItemKey.value = index;
|
|
||||||
emit('change', { value: item, key: index });
|
|
||||||
};
|
|
||||||
const icon = getPropsSlot(slots, props, 'icon');
|
|
||||||
const title = getPropsSlot(slots, props, 'title');
|
|
||||||
const iconNode = index => {
|
|
||||||
return icon ? (
|
|
||||||
<span class={classNames({ [`${pre}-item-icon`]: icon })}>{slots.icon?.(index)}</span>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
);
|
|
||||||
};
|
|
||||||
const itemNode = (item, index) => {
|
|
||||||
if (title) {
|
|
||||||
return <div>{slots.title?.(index)}</div>;
|
|
||||||
}
|
|
||||||
return <span>{item.value}</span>;
|
|
||||||
};
|
|
||||||
const calcThumbStyle = index => {
|
|
||||||
return {
|
|
||||||
left: itemRef.value[index].children[0].offsetParent.offsetLeft,
|
|
||||||
width: itemRef.value[index].children[0].clientWidth,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
const thumbStyle = reactive({
|
|
||||||
transform: '',
|
|
||||||
width: '',
|
|
||||||
});
|
|
||||||
const isValueType = item => {
|
|
||||||
return item instanceof Object ? (item.disabled ? true : false) : false;
|
|
||||||
};
|
|
||||||
watch(
|
|
||||||
() => currentItemKey.value,
|
|
||||||
(newValue, oldValue) => {
|
|
||||||
const prev = oldValue ? oldValue : defaultValue.value ? defaultValue.value : 0;
|
|
||||||
const next = newValue;
|
|
||||||
const calcPrevStyle = calcThumbStyle(prev);
|
|
||||||
const calcNextStyle = calcThumbStyle(next);
|
|
||||||
mergedStyle.startLeft = toPX(calcPrevStyle.left);
|
|
||||||
mergedStyle.startWidth = toPX(calcPrevStyle.width);
|
|
||||||
mergedStyle.activeLeft = toPX(calcNextStyle.left);
|
|
||||||
mergedStyle.activeWidth = toPX(calcNextStyle.width);
|
|
||||||
if (prev !== next) {
|
|
||||||
thumbStyle.transform = `translateX(${mergedStyle.activeLeft})`;
|
|
||||||
thumbStyle.width = `${mergedStyle.activeWidth}`;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
const thumbNode = () => {
|
|
||||||
return thumbShow.value ? (
|
|
||||||
<div
|
|
||||||
class={classNames({
|
|
||||||
[`${pre}-thumb`]: thumbShow.value,
|
|
||||||
[`${pre}-thumb-motion-appear-active`]: thumbShow.value,
|
|
||||||
})}
|
|
||||||
style={thumbStyle}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
''
|
|
||||||
);
|
|
||||||
};
|
|
||||||
return () => {
|
|
||||||
return wrapSSR(
|
|
||||||
<div
|
|
||||||
class={classNames(pre, {
|
|
||||||
[hashId.value]: true,
|
|
||||||
[`${pre}-block`]: props.block,
|
|
||||||
[`${pre}-item-disabled`]: props.disabled,
|
|
||||||
[`${pre}-lg`]: size.value == 'large',
|
|
||||||
[`${pre}-sm`]: size.value == 'small',
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<div class={classNames(`${pre}-group`)}>
|
|
||||||
{thumbNode()}
|
|
||||||
{options.value.map((item, index) => {
|
|
||||||
return (
|
return (
|
||||||
<label
|
<label
|
||||||
ref={ref => (itemRef.value[index] = ref)}
|
class={classNames(
|
||||||
class={classNames(`${pre}-item`, {
|
{
|
||||||
[`${pre}-item-selected`]: currentItemKey.value == index,
|
[`${prefixCls}-item-disabled`]: disabled,
|
||||||
[`${pre}-item-disabled`]: disabled.value || isValueType(item),
|
},
|
||||||
})}
|
attrs.class,
|
||||||
onClick={() => handleSelectedChange(item, index)}
|
)}
|
||||||
>
|
>
|
||||||
{isDisabled(item)}
|
<input
|
||||||
<div class={classNames(`${pre}-item-label`)} key={index}>
|
class={`${prefixCls}-item-input`}
|
||||||
{iconNode(index)}
|
type="radio"
|
||||||
{typeof item == 'object' ? itemNode(item, index) : item}
|
disabled={disabled}
|
||||||
|
checked={checked}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
<div class={`${prefixCls}-item-label`} title={typeof title === 'string' ? title : ''}>
|
||||||
|
{typeof label === 'function'
|
||||||
|
? label({
|
||||||
|
value,
|
||||||
|
disabled,
|
||||||
|
payload,
|
||||||
|
title,
|
||||||
|
})
|
||||||
|
: label}
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
SegmentedOption.inheritAttrs = false;
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'ASegmented',
|
||||||
|
inheritAttrs: false,
|
||||||
|
props: initDefaultProps(segmentedProps(), {
|
||||||
|
options: [],
|
||||||
|
motionName: 'thumb-motion',
|
||||||
|
}),
|
||||||
|
emits: ['change', 'update:value'],
|
||||||
|
slots: ['label'],
|
||||||
|
setup(props, { emit, slots, attrs }) {
|
||||||
|
const { prefixCls, direction, size } = useConfigInject('segmented', props);
|
||||||
|
const [wrapSSR, hashId] = useStyle(prefixCls);
|
||||||
|
const rootRef = ref<HTMLDivElement>();
|
||||||
|
const thumbShow = ref(false);
|
||||||
|
|
||||||
|
const segmentedOptions = computed(() => normalizeOptions(props.options));
|
||||||
|
const handleChange = (_event: ChangeEvent, val: SegmentedValue) => {
|
||||||
|
if (props.disabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
emit('update:value', val);
|
||||||
|
emit('change', val);
|
||||||
|
};
|
||||||
|
return () => {
|
||||||
|
const pre = prefixCls.value;
|
||||||
|
return wrapSSR(
|
||||||
|
<div
|
||||||
|
{...attrs}
|
||||||
|
class={classNames(
|
||||||
|
pre,
|
||||||
|
{
|
||||||
|
[hashId.value]: true,
|
||||||
|
[`${pre}-block`]: props.block,
|
||||||
|
[`${pre}-disabled`]: props.disabled,
|
||||||
|
[`${pre}-lg`]: size.value == 'large',
|
||||||
|
[`${pre}-sm`]: size.value == 'small',
|
||||||
|
[`${pre}-rtl`]: direction.value === 'rtl',
|
||||||
|
},
|
||||||
|
attrs.class,
|
||||||
|
)}
|
||||||
|
ref={rootRef}
|
||||||
|
>
|
||||||
|
<div class={`${pre}-group`}>
|
||||||
|
<MotionThumb
|
||||||
|
containerRef={rootRef}
|
||||||
|
prefixCls={pre}
|
||||||
|
value={props.value}
|
||||||
|
motionName={`${pre}-${props.motionName}`}
|
||||||
|
direction={direction.value}
|
||||||
|
getValueIndex={val => segmentedOptions.value.findIndex(n => n.value === val)}
|
||||||
|
onMotionStart={() => {
|
||||||
|
thumbShow.value = true;
|
||||||
|
}}
|
||||||
|
onMotionEnd={() => {
|
||||||
|
thumbShow.value = false;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{segmentedOptions.value.map(segmentedOption => (
|
||||||
|
<SegmentedOption
|
||||||
|
key={segmentedOption.value}
|
||||||
|
prefixCls={pre}
|
||||||
|
class={classNames(segmentedOption.className, `${pre}-item`, {
|
||||||
|
[`${pre}-item-selected`]:
|
||||||
|
segmentedOption.value === props.value && !thumbShow.value,
|
||||||
})}
|
})}
|
||||||
|
checked={segmentedOption.value === props.value}
|
||||||
|
onChange={handleChange}
|
||||||
|
{...segmentedOption}
|
||||||
|
disabled={!!props.disabled || !!segmentedOption.disabled}
|
||||||
|
v-slots={slots}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>,
|
</div>,
|
||||||
);
|
);
|
||||||
|
|
|
@ -17,7 +17,7 @@ interface SegmentedToken extends FullToken<'Segmented'> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================== Mixins ==============================
|
// ============================== Mixins ==============================
|
||||||
function segmentedDisabledItem(cls: string, token: SegmentedToken): CSSObject {
|
function getItemDisabledStyle(cls: string, token: SegmentedToken): CSSObject {
|
||||||
return {
|
return {
|
||||||
[`${cls}, ${cls}:hover, ${cls}:focus`]: {
|
[`${cls}, ${cls}:hover, ${cls}:focus`]: {
|
||||||
color: token.colorTextDisabled,
|
color: token.colorTextDisabled,
|
||||||
|
@ -26,7 +26,7 @@ function segmentedDisabledItem(cls: string, token: SegmentedToken): CSSObject {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSegmentedItemSelectedStyle(token: SegmentedToken): CSSObject {
|
function getItemSelectedStyle(token: SegmentedToken): CSSObject {
|
||||||
return {
|
return {
|
||||||
backgroundColor: token.bgColorSelected,
|
backgroundColor: token.bgColorSelected,
|
||||||
boxShadow: token.boxShadow,
|
boxShadow: token.boxShadow,
|
||||||
|
@ -39,8 +39,8 @@ const segmentedTextEllipsisCss: CSSObject = {
|
||||||
...textEllipsis,
|
...textEllipsis,
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================== Shared ==============================
|
// ============================== Styles ==============================
|
||||||
const genSharedSegmentedStyle: GenerateStyle<SegmentedToken> = (token): CSSObject => {
|
const genSegmentedStyle: GenerateStyle<SegmentedToken> = (token: SegmentedToken) => {
|
||||||
const { componentCls } = token;
|
const { componentCls } = token;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -63,16 +63,16 @@ const genSharedSegmentedStyle: GenerateStyle<SegmentedToken> = (token): CSSObjec
|
||||||
},
|
},
|
||||||
|
|
||||||
// RTL styles
|
// RTL styles
|
||||||
'&&-rtl': {
|
[`&${componentCls}-rtl`]: {
|
||||||
direction: 'rtl',
|
direction: 'rtl',
|
||||||
},
|
},
|
||||||
|
|
||||||
// block styles
|
// block styles
|
||||||
'&&-block': {
|
[`&${componentCls}-block`]: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
},
|
},
|
||||||
|
|
||||||
[`&&-block ${componentCls}-item`]: {
|
[`&${componentCls}-block ${componentCls}-item`]: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: 0,
|
minWidth: 0,
|
||||||
},
|
},
|
||||||
|
@ -86,7 +86,7 @@ const genSharedSegmentedStyle: GenerateStyle<SegmentedToken> = (token): CSSObjec
|
||||||
borderRadius: token.borderRadiusSM,
|
borderRadius: token.borderRadiusSM,
|
||||||
|
|
||||||
'&-selected': {
|
'&-selected': {
|
||||||
...getSegmentedItemSelectedStyle(token),
|
...getItemSelectedStyle(token),
|
||||||
color: token.labelColorHover,
|
color: token.labelColorHover,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ const genSharedSegmentedStyle: GenerateStyle<SegmentedToken> = (token): CSSObjec
|
||||||
height: '100%',
|
height: '100%',
|
||||||
top: 0,
|
top: 0,
|
||||||
insetInlineStart: 0,
|
insetInlineStart: 0,
|
||||||
borderRadius: token.borderRadiusSM,
|
borderRadius: 'inherit',
|
||||||
transition: `background-color ${token.motionDurationMid}`,
|
transition: `background-color ${token.motionDurationMid}`,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@ const genSharedSegmentedStyle: GenerateStyle<SegmentedToken> = (token): CSSObjec
|
||||||
|
|
||||||
// syntactic sugar to add `icon` for Segmented Item
|
// syntactic sugar to add `icon` for Segmented Item
|
||||||
'&-icon + *': {
|
'&-icon + *': {
|
||||||
marginInlineEnd: token.marginSM / 2,
|
marginInlineStart: token.marginSM / 2,
|
||||||
},
|
},
|
||||||
|
|
||||||
'&-input': {
|
'&-input': {
|
||||||
|
@ -132,39 +132,9 @@ const genSharedSegmentedStyle: GenerateStyle<SegmentedToken> = (token): CSSObjec
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// size styles
|
|
||||||
'&&-lg': {
|
|
||||||
borderRadius: token.borderRadiusLG,
|
|
||||||
[`${componentCls}-item-label`]: {
|
|
||||||
minHeight: token.controlHeightLG - token.segmentedContainerPadding * 2,
|
|
||||||
lineHeight: `${token.controlHeightLG - token.segmentedContainerPadding * 2}px`,
|
|
||||||
padding: `0 ${token.segmentedPaddingHorizontal}px`,
|
|
||||||
fontSize: token.fontSizeLG,
|
|
||||||
},
|
|
||||||
[`${componentCls}-item-selected`]: {
|
|
||||||
borderRadius: token.borderRadius,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
'&&-sm': {
|
|
||||||
borderRadius: token.borderRadiusSM,
|
|
||||||
[`${componentCls}-item-label`]: {
|
|
||||||
minHeight: token.controlHeightSM - token.segmentedContainerPadding * 2,
|
|
||||||
lineHeight: `${token.controlHeightSM - token.segmentedContainerPadding * 2}px`,
|
|
||||||
padding: `0 ${token.segmentedPaddingHorizontalSM}px`,
|
|
||||||
},
|
|
||||||
[`${componentCls}-item-selected`]: {
|
|
||||||
borderRadius: token.borderRadiusXS,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// disabled styles
|
|
||||||
...segmentedDisabledItem(`&-disabled ${componentCls}-item`, token),
|
|
||||||
...segmentedDisabledItem(`${componentCls}-item-disabled`, token),
|
|
||||||
|
|
||||||
// thumb styles
|
// thumb styles
|
||||||
[`${componentCls}-thumb`]: {
|
[`${componentCls}-thumb`]: {
|
||||||
...getSegmentedItemSelectedStyle(token),
|
...getItemSelectedStyle(token),
|
||||||
|
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
insetBlockStart: 0,
|
insetBlockStart: 0,
|
||||||
|
@ -180,6 +150,36 @@ const genSharedSegmentedStyle: GenerateStyle<SegmentedToken> = (token): CSSObjec
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// size styles
|
||||||
|
[`&${componentCls}-lg`]: {
|
||||||
|
borderRadius: token.borderRadiusLG,
|
||||||
|
[`${componentCls}-item-label`]: {
|
||||||
|
minHeight: token.controlHeightLG - token.segmentedContainerPadding * 2,
|
||||||
|
lineHeight: `${token.controlHeightLG - token.segmentedContainerPadding * 2}px`,
|
||||||
|
padding: `0 ${token.segmentedPaddingHorizontal}px`,
|
||||||
|
fontSize: token.fontSizeLG,
|
||||||
|
},
|
||||||
|
[`${componentCls}-item, ${componentCls}-thumb`]: {
|
||||||
|
borderRadius: token.borderRadius,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
[`&${componentCls}-sm`]: {
|
||||||
|
borderRadius: token.borderRadiusSM,
|
||||||
|
[`${componentCls}-item-label`]: {
|
||||||
|
minHeight: token.controlHeightSM - token.segmentedContainerPadding * 2,
|
||||||
|
lineHeight: `${token.controlHeightSM - token.segmentedContainerPadding * 2}px`,
|
||||||
|
padding: `0 ${token.segmentedPaddingHorizontalSM}px`,
|
||||||
|
},
|
||||||
|
[`${componentCls}-item, ${componentCls}-thumb`]: {
|
||||||
|
borderRadius: token.borderRadiusXS,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// disabled styles
|
||||||
|
...getItemDisabledStyle(`&-disabled ${componentCls}-item`, token),
|
||||||
|
...getItemDisabledStyle(`${componentCls}-item-disabled`, token),
|
||||||
|
|
||||||
// transition effect when `appear-active`
|
// transition effect when `appear-active`
|
||||||
[`${componentCls}-thumb-motion-appear-active`]: {
|
[`${componentCls}-thumb-motion-appear-active`]: {
|
||||||
transition: `transform ${token.motionDurationSlow} ${token.motionEaseInOut}, width ${token.motionDurationSlow} ${token.motionEaseInOut}`,
|
transition: `transform ${token.motionDurationSlow} ${token.motionEaseInOut}, width ${token.motionDurationSlow} ${token.motionEaseInOut}`,
|
||||||
|
@ -188,6 +188,7 @@ const genSharedSegmentedStyle: GenerateStyle<SegmentedToken> = (token): CSSObjec
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================== Export ==============================
|
// ============================== Export ==============================
|
||||||
export default genComponentStyleHook('Segmented', token => {
|
export default genComponentStyleHook('Segmented', token => {
|
||||||
const {
|
const {
|
||||||
|
@ -210,5 +211,5 @@ export default genComponentStyleHook('Segmented', token => {
|
||||||
bgColorHover: colorFillSecondary,
|
bgColorHover: colorFillSecondary,
|
||||||
bgColorSelected: colorBgElevated,
|
bgColorSelected: colorBgElevated,
|
||||||
});
|
});
|
||||||
return [genSharedSegmentedStyle(segmentedToken)];
|
return [genSegmentedStyle(segmentedToken)];
|
||||||
});
|
});
|
||||||
|
|
|
@ -163,6 +163,8 @@ declare module 'vue' {
|
||||||
|
|
||||||
ASelect: typeof import('ant-design-vue')['Select'];
|
ASelect: typeof import('ant-design-vue')['Select'];
|
||||||
|
|
||||||
|
ASegmented: typeof import('ant-design-vue')['Segmented'];
|
||||||
|
|
||||||
ASelectOptGroup: typeof import('ant-design-vue')['SelectOptGroup'];
|
ASelectOptGroup: typeof import('ant-design-vue')['SelectOptGroup'];
|
||||||
|
|
||||||
ASelectOption: typeof import('ant-design-vue')['SelectOption'];
|
ASelectOption: typeof import('ant-design-vue')['SelectOption'];
|
||||||
|
|
Loading…
Reference in New Issue