diff --git a/src/_common b/src/_common index 639bad74b..951b20f9e 160000 --- a/src/_common +++ b/src/_common @@ -1 +1 @@ -Subproject commit 639bad74bddb81b0f3bb084bea790359684bfb84 +Subproject commit 951b20f9ed2f1e7f64ea976f93f8750072602046 diff --git a/src/slider/slider-button.tsx b/src/slider/slider-button.tsx index 3e9f85afc..85f001146 100644 --- a/src/slider/slider-button.tsx +++ b/src/slider/slider-button.tsx @@ -1,15 +1,17 @@ -import Vue, { VNode, PropType, VueConstructor } from 'vue'; -import { Styles } from '../common'; -import Slider from './slider'; -import Tooltip from '../tooltip/index'; -import ITooltip from '../tooltip/tooltip'; +import Vue, { PropType, VNode, VueConstructor } from 'vue'; + +import { formatLabel, formatPrecision } from '../_common/js/slider/utils'; import { getIEVersion } from '../_common/js/utils/helper'; -import { TdSliderProps } from './type'; -import { TdTooltipProps } from '../tooltip/type'; -import { renderTNodeJSX } from '../utils/render-tnode'; import { getClassPrefixMixins } from '../config-provider/config-receiver'; -import { formatLabel } from '../_common/js/slider/utils'; +import Tooltip from '../tooltip/index'; +import ITooltip from '../tooltip/tooltip'; import mixins from '../utils/mixins'; +import { renderTNodeJSX } from '../utils/render-tnode'; +import Slider from './slider'; + +import type { Styles } from '../common'; +import type { TdTooltipProps } from '../tooltip/type'; +import type { TdSliderProps } from './type'; const classPrefixMixins = getClassPrefixMixins('slider__button'); @@ -301,7 +303,7 @@ export default mixins(classPrefixMixins, Vue as VueConstructor { this.showTooltipComponent(); diff --git a/src/slider/slider.tsx b/src/slider/slider.tsx index f95a84ed1..ac0ec6250 100644 --- a/src/slider/slider.tsx +++ b/src/slider/slider.tsx @@ -1,13 +1,16 @@ import Vue, { VNode } from 'vue'; + +import { formatPrecision } from '../_common/js/slider/utils'; +import { getClassPrefixMixins } from '../config-provider/config-receiver'; +import InputNumber from '../input-number/index'; import { emitEvent } from '../utils/event'; -import { TNode, ClassName } from '../common'; +import mixins from '../utils/mixins'; import props from './props'; -import InputNumber from '../input-number/index'; -import TSliderMark from './slider-mark'; -import { SliderValue, TdSliderProps } from './type'; import TSliderButton from './slider-button'; -import { getClassPrefixMixins } from '../config-provider/config-receiver'; -import mixins from '../utils/mixins'; +import TSliderMark from './slider-mark'; + +import type { ClassName, TNode } from '../common'; +import type { SliderValue, TdSliderProps } from './type'; const classPrefixMixins = getClassPrefixMixins('slider'); @@ -186,9 +189,10 @@ export default mixins(classPrefixMixins).extend({ value(newVal: SliderValue) { if (this.dragging === true) return; if (Array.isArray(newVal) && this.range) { - [this.firstValue, this.secondValue] = newVal; + this.firstValue = formatPrecision(newVal[0], this.precision); + this.secondValue = formatPrecision(newVal[1], this.precision); } else { - this.prevValue = newVal as number; + this.prevValue = formatPrecision(newVal as number, this.precision); } }, dragging(newVal: boolean) { @@ -211,18 +215,18 @@ export default mixins(classPrefixMixins).extend({ const { min, max, value } = this; if (this.range) { if (Array.isArray(value)) { - this.firstValue = Math.max(min || 0, value[0] ?? 0); - this.secondValue = Math.min(max || 100, value[1] ?? 0); + this.firstValue = formatPrecision(Math.max(min || 0, value[0] ?? 0), this.precision); + this.secondValue = formatPrecision(Math.min(max || 100, value[1] ?? 0), this.precision); } else { - this.firstValue = min || 0; - this.secondValue = max || 100; + this.firstValue = formatPrecision(min || 0, this.precision); + this.secondValue = formatPrecision(max || 100, this.precision); } valuetext = `${this.firstValue}-${this.secondValue}`; } else { if (typeof this.value !== 'number') { - this.prevValue = min; + this.prevValue = formatPrecision(min, this.precision); } else { - this.prevValue = Math.min(max, Math.max(min, value as number)); + this.prevValue = formatPrecision(Math.min(max, Math.max(min, value as number)), this.precision); } valuetext = String(this.prevValue); } @@ -250,12 +254,12 @@ export default mixins(classPrefixMixins).extend({ if (firstValue < min) firstValue = min; if (secondValue < min) secondValue = this.secondValue; if (secondValue > max) secondValue = max; - return [firstValue, secondValue]; + return [formatPrecision(firstValue, this.precision), formatPrecision(secondValue, this.precision)]; } let prevValue = value as number; if (prevValue < min) prevValue = min; if (prevValue > max) prevValue = max; - return prevValue; + return formatPrecision(prevValue, this.precision); }, // 相应button的位置 setPosition(percent: number): void { diff --git a/src/swiper/__tests__/__snapshots__/index.test.jsx.snap b/src/swiper/__tests__/__snapshots__/index.test.jsx.snap index bd5cedc64..7addd96c9 100644 --- a/src/swiper/__tests__/__snapshots__/index.test.jsx.snap +++ b/src/swiper/__tests__/__snapshots__/index.test.jsx.snap @@ -197,28 +197,6 @@ exports[`Swiper > :props > :navigation 1`] = ` `; -exports[`Swiper > :props > :theme 1`] = ` -
-
-
-
-
-
    -
-
-`; - exports[`Swiper > :props > :type 1`] = `
{ }); expect(wrapper.element).toMatchSnapshot(); }); - it(':theme', () => { - const wrapper = mount({ - render() { - return ( - - 1 - 2 - - ); - }, - }); - expect(wrapper.element).toMatchSnapshot(); - }); it(':type', () => { const wrapper = mount({ render() { diff --git a/src/swiper/_example/card.vue b/src/swiper/_example/card.vue index 236d49a8c..3345752d5 100644 --- a/src/swiper/_example/card.vue +++ b/src/swiper/_example/card.vue @@ -1,24 +1,33 @@ + + diff --git a/src/swiper/_usage/props.json b/src/swiper/_usage/props.json index 01cfb7450..0f54d827c 100644 --- a/src/swiper/_usage/props.json +++ b/src/swiper/_usage/props.json @@ -47,21 +47,6 @@ "defaultValue": true, "options": [] }, - { - "name": "theme", - "type": "enum", - "defaultValue": "light", - "options": [ - { - "label": "light", - "value": "light" - }, - { - "label": "dark", - "value": "dark" - } - ] - }, { "name": "trigger", "type": "enum", diff --git a/src/swiper/props.ts b/src/swiper/props.ts index f22b9f479..e7c190511 100644 --- a/src/swiper/props.ts +++ b/src/swiper/props.ts @@ -22,6 +22,11 @@ export default { type: Boolean, default: true, }, + /** 卡片模式下非当前展示轮播图的缩放比例 */ + cardScale: { + type: Number, + default: 210 / 332, + }, /** 当前轮播在哪一项(下标) */ current: { type: Number, @@ -69,15 +74,6 @@ export default { type: Boolean, default: true, }, - /** 深色模式和浅色模式 */ - theme: { - type: String as PropType, - default: 'light' as TdSwiperProps['theme'], - validator(val: TdSwiperProps['theme']): boolean { - if (!val) return true; - return ['light', 'dark'].includes(val); - }, - }, /** 触发切换的方式:悬浮、点击等 */ trigger: { type: String as PropType, diff --git a/src/swiper/swiper-item.tsx b/src/swiper/swiper-item.tsx index 253633c82..75b18bccd 100644 --- a/src/swiper/swiper-item.tsx +++ b/src/swiper/swiper-item.tsx @@ -1,10 +1,13 @@ import { VNode } from 'vue'; -import props from './props'; import { getClassPrefixMixins } from '../config-provider/config-receiver'; import mixins from '../utils/mixins'; +import props from './props'; const classPrefixMixins = getClassPrefixMixins('swiper'); +const CARD_SCALE = 210 / 332; // 缩放比例 +const ITEM_WIDTH = 0.415; // 依据设计稿使用t-swiper__card控制每个swiper的宽度为41.5% + const swiperItemProps = { index: { type: Number, @@ -12,20 +15,23 @@ const swiperItemProps = { currentIndex: { type: Number, }, + cardScale: { + type: Number, + default: CARD_SCALE, + }, isSwitching: { type: Boolean, default: false, }, - getWrapAttribute: { - type: Function, + swiperWidth: { + type: Number, + default: 0, }, swiperItemLength: { type: Number, default: 0, }, }; -const CARD_SCALE = 210 / 332; // 缩放比例 -const itemWidth = 0.415; // 依据设计稿使用t-swiper__card控制每个swiper的宽度为41.5% export default mixins(classPrefixMixins).extend({ name: 'TSwiperItem', @@ -58,16 +64,19 @@ export default mixins(classPrefixMixins).extend({ }, translateX(): number { if (this.type !== 'card') return 0; - const wrapWidth = this.getWrapAttribute('offsetWidth'); + const { swiperWidth } = this; const translateIndex = !this.active && this.swiperItemLength > 2 ? this.disposeIndex : this.index; const inStage = Math.abs(translateIndex - this.currentIndex) <= 1; if (inStage) { - return (wrapWidth * ((translateIndex - this.currentIndex) * (1 - itemWidth * CARD_SCALE) - itemWidth + 1)) / 2; + return ( + (swiperWidth * ((translateIndex - this.currentIndex) * (1 - ITEM_WIDTH * this.cardScale) - ITEM_WIDTH + 1)) + / 2 + ); } if (translateIndex < this.currentIndex) { - return (-itemWidth * (1 + CARD_SCALE) * wrapWidth) / 2; + return (-ITEM_WIDTH * (1 + this.cardScale) * swiperWidth) / 2; } - return ((2 + itemWidth * (CARD_SCALE - 1)) * wrapWidth) / 2; + return ((2 + ITEM_WIDTH * (this.cardScale - 1)) * swiperWidth) / 2; }, zIndex(): number { if (this.type !== 'card') return 0; @@ -94,7 +103,7 @@ export default mixins(classPrefixMixins).extend({ const translateIndex = !this.active && this.swiperItemLength > 2 ? this.disposeIndex : this.index; const isActivity = translateIndex === this.currentIndex; return { - transform: `translateX(${this.translateX}px) scale(${isActivity ? 1 : CARD_SCALE})`, + transform: `translateX(${this.translateX}px) scale(${isActivity ? 1 : this.cardScale})`, transition: `transform ${this.duration / 1000}s ease`, zIndex: this.zIndex, }; diff --git a/src/swiper/swiper.en-US.md b/src/swiper/swiper.en-US.md index a6a367226..43b1e838a 100644 --- a/src/swiper/swiper.en-US.md +++ b/src/swiper/swiper.en-US.md @@ -1,25 +1,26 @@ :: BASE_DOC :: ## API + ### Swiper Props name | type | default | description | required -- | -- | -- | -- | -- -animation | String | slide | options:slide/fade | N +animation | String | slide | options: slide/fade | N autoplay | Boolean | true | \- | N +cardScale | Number | 210/332 | \- | N current | Number | 0 | `v-model` is supported | N defaultCurrent | Number | 0 | uncontrolled property | N -direction | String | horizontal | options:horizontal/vertical | N +direction | String | horizontal | options: horizontal/vertical | N duration | Number | 300 | \- | N height | Number | - | \- | N interval | Number | 5000 | \- | N loop | Boolean | true | \- | N -navigation | Object / Slot / Function | - | Typescript:`SwiperNavigation \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N +navigation | Object / Slot / Function | - | Typescript: `SwiperNavigation \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N stopOnHover | Boolean | true | \- | N -theme | String | light | options:light/dark | N -trigger | String | hover | options:hover/click | N -type | String | default | options:default/card | N -onChange | Function | | Typescript:`(current: number, context: { source: SwiperChangeSource }) => void`
[see more ts definition](https://github.com/Tencent/tdesign-vue/tree/develop/src/swiper/type.ts)。
`type SwiperChangeSource = 'autoplay' \| 'click' \| 'hover'`
| N +trigger | String | hover | options: hover/click | N +type | String | default | options: default/card | N +onChange | Function | | Typescript: `(current: number, context: { source: SwiperChangeSource }) => void`
[see more ts definition](https://github.com/Tencent/tdesign-vue/tree/develop/src/swiper/type.ts)。
`type SwiperChangeSource = 'autoplay' \| 'click' \| 'hover'`
| N ### Swiper Events @@ -31,7 +32,7 @@ change | `(current: number, context: { source: SwiperChangeSource })` | [see mor name | type | default | description | required -- | -- | -- | -- | -- -placement | String | inside | options:inside/outside | N -showSlideBtn | String | always | options:always/hover/never | N -size | String | medium | options:small/medium/large | N -type | String | - | Typescript:`SwiperNavigationType` `type SwiperNavigationType = 'dots' \| 'dots-bar' \| 'bars' \| 'fraction'`。[see more ts definition](https://github.com/Tencent/tdesign-vue/tree/develop/src/swiper/type.ts) | N +placement | String | inside | options: inside/outside | N +showSlideBtn | String | always | options: always/hover/never | N +size | String | medium | options: small/medium/large | N +type | String | - | Typescript: `SwiperNavigationType` `type SwiperNavigationType = 'dots' \| 'dots-bar' \| 'bars' \| 'fraction'`。[see more ts definition](https://github.com/Tencent/tdesign-vue/tree/develop/src/swiper/type.ts) | N diff --git a/src/swiper/swiper.md b/src/swiper/swiper.md index e7ef64fe8..9cbae0277 100644 --- a/src/swiper/swiper.md +++ b/src/swiper/swiper.md @@ -1,12 +1,14 @@ :: BASE_DOC :: ## API + ### Swiper Props -名称 | 类型 | 默认值 | 说明 | 必传 +名称 | 类型 | 默认值 | 描述 | 必传 -- | -- | -- | -- | -- animation | String | slide | 轮播切换动画效果类型:滑动、淡入淡出等。可选项:slide/fade | N autoplay | Boolean | true | 是否自动播放 | N +cardScale | Number | 210/332 | 卡片模式下非当前展示轮播图的缩放比例 | N current | Number | 0 | 当前轮播在哪一项(下标)。支持语法糖 `v-model` | N defaultCurrent | Number | 0 | 当前轮播在哪一项(下标)。非受控属性 | N direction | String | horizontal | 轮播滑动方向,包括横向滑动和纵向滑动两个方向。可选项:horizontal/vertical | N @@ -16,7 +18,6 @@ interval | Number | 5000 | 轮播间隔时间 | N loop | Boolean | true | 是否循环播放 | N navigation | Object / Slot / Function | - | 导航器全部配置。TS 类型:`SwiperNavigation \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N stopOnHover | Boolean | true | 是否悬浮时停止轮播 | N -theme | String | light | 深色模式和浅色模式。可选项:light/dark | N trigger | String | hover | 触发切换的方式:悬浮、点击等。可选项:hover/click | N type | String | default | 样式类型:默认样式、卡片样式。可选项:default/card | N onChange | Function | | TS 类型:`(current: number, context: { source: SwiperChangeSource }) => void`
轮播切换时触发。[详细类型定义](https://github.com/Tencent/tdesign-vue/tree/develop/src/swiper/type.ts)。
`type SwiperChangeSource = 'autoplay' \| 'click' \| 'hover'`
| N @@ -29,7 +30,7 @@ change | `(current: number, context: { source: SwiperChangeSource })` | 轮播 ### SwiperNavigation -名称 | 类型 | 默认值 | 说明 | 必传 +名称 | 类型 | 默认值 | 描述 | 必传 -- | -- | -- | -- | -- placement | String | inside | 导航器位置,位于主体的内侧或是外侧。可选项:inside/outside | N showSlideBtn | String | always | 何时显示导航器的翻页按钮:始终显示、悬浮显示、永不显示。可选项:always/hover/never | N diff --git a/src/swiper/swiper.tsx b/src/swiper/swiper.tsx index b8ea6c74e..c124f2562 100644 --- a/src/swiper/swiper.tsx +++ b/src/swiper/swiper.tsx @@ -2,14 +2,16 @@ import Vue, { VNode, VNodeComponentOptions, VueConstructor } from 'vue'; import { ChevronLeftIcon as TdChevronLeftIcon, ChevronRightIcon as TdChevronRightIcon } from 'tdesign-icons-vue'; import { kebabCase } from 'lodash-es'; -import props from './props'; -import { TdSwiperProps, SwiperNavigation, SwiperChangeSource } from './type'; -import TSwiperItem from './swiper-item'; +import { getClassPrefixMixins, getGlobalIconMixins } from '../config-provider/config-receiver'; import { isVNode } from '../hooks/render-tnode'; -import { renderTNodeJSX } from '../utils/render-tnode'; + import { emitEvent } from '../utils/event'; -import { getClassPrefixMixins, getGlobalIconMixins } from '../config-provider/config-receiver'; import mixins from '../utils/mixins'; +import { renderTNodeJSX } from '../utils/render-tnode'; +import props from './props'; +import TSwiperItem from './swiper-item'; + +import type { SwiperChangeSource, SwiperNavigation, TdSwiperProps } from './type'; const classPrefixMixins = getClassPrefixMixins('swiper'); @@ -42,6 +44,7 @@ export default mixins(Vue as VueConstructor, classPrefixMixins, getGl isHovering: false, isSwitching: false, swiperItemList: [] as Array, + swiperOffset: { width: 0, height: 0 }, showArrow: false, }; }, @@ -76,7 +79,7 @@ export default mixins(Vue as VueConstructor, classPrefixMixins, getGl }; }, containerStyle(): any { - const offsetHeight = this.height ? `${this.height}px` : `${this.getWrapAttribute('offsetHeight')}px`; + const offsetHeight = this.height ? `${this.height}px` : `${this.swiperOffset.height}px`; if (this.type === 'card' || this.animation === 'fade') { return { height: offsetHeight, @@ -105,8 +108,9 @@ export default mixins(Vue as VueConstructor, classPrefixMixins, getGl index={index} currentIndex={this.currentIndex} isSwitching={this.isSwitching} - getWrapAttribute={this.getWrapAttribute} + cardScale={this.cardScale} swiperItemLength={this.swiperItemLength} + swiperWidth={this.swiperOffset.width} props={{ ...this.$props, ...swiperItem.propsData }} > {swiperItem.children} @@ -140,6 +144,17 @@ export default mixins(Vue as VueConstructor, classPrefixMixins, getGl this.updateSwiperItems(); this.setTimer(); this.showArrow = this.navigationConfig.showSlideBtn === 'always'; + + const resizeObserver = new ResizeObserver(([entry]) => { + const parent = entry.target.parentNode as HTMLElement; + if (parent) { + this.swiperOffset = { + width: parent.offsetWidth, + height: parent.offsetHeight, + }; + } + }); + resizeObserver.observe(this.$refs.swiperWrap as HTMLElement); }, updated() { @@ -224,9 +239,6 @@ export default mixins(Vue as VueConstructor, classPrefixMixins, getGl } return this.swiperTo(this.currentIndex - 1, context); }, - getWrapAttribute(attr: string) { - return (this.$refs.swiperWrap as Element)?.parentNode?.[attr]; - }, renderPagination() { const fractionIndex = this.currentIndex + 1 > this.swiperItemLength ? 1 : this.currentIndex + 1; const { ChevronLeftIcon, ChevronRightIcon } = this.useGlobalIcon({ diff --git a/src/swiper/type.ts b/src/swiper/type.ts index 0b7309245..2e1dda33d 100644 --- a/src/swiper/type.ts +++ b/src/swiper/type.ts @@ -17,6 +17,11 @@ export interface TdSwiperProps { * @default true */ autoplay?: boolean; + /** + * 卡片模式下非当前展示轮播图的缩放比例 + * @default 210/332 + */ + cardScale?: number; /** * 当前轮播在哪一项(下标) * @default 0 @@ -60,11 +65,6 @@ export interface TdSwiperProps { * @default true */ stopOnHover?: boolean; - /** - * 深色模式和浅色模式 - * @default light - */ - theme?: 'light' | 'dark'; /** * 触发切换的方式:悬浮、点击等 * @default hover diff --git a/test/snap/__snapshots__/csr.test.js.snap b/test/snap/__snapshots__/csr.test.js.snap index 2f92e2c89..4af30a364 100644 --- a/test/snap/__snapshots__/csr.test.js.snap +++ b/test/snap/__snapshots__/csr.test.js.snap @@ -109236,22 +109236,106 @@ exports[`csr snapshot test > csr test ./src/swiper/_example/card.vue 1`] = ` class="tdesign-demo-block--swiper" >
+ class="t-space-item" + > +
+ + 卡片缩放比例 + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Default +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
    +
-
diff --git a/test/snap/__snapshots__/ssr.test.js.snap b/test/snap/__snapshots__/ssr.test.js.snap index 9354f29f9..cc49a44ef 100644 --- a/test/snap/__snapshots__/ssr.test.js.snap +++ b/test/snap/__snapshots__/ssr.test.js.snap @@ -1007,7 +1007,7 @@ exports[`ssr snapshot test > renders ./src/sticky-tool/_example/shape.vue correc exports[`ssr snapshot test > renders ./src/swiper/_example/base.vue correctly 1`] = `"
    "`; -exports[`ssr snapshot test > renders ./src/swiper/_example/card.vue correctly 1`] = `"
      "`; +exports[`ssr snapshot test > renders ./src/swiper/_example/card.vue correctly 1`] = `"
      卡片缩放比例
      Default
        "`; exports[`ssr snapshot test > renders ./src/swiper/_example/current.vue correctly 1`] = `"
          "`;