Skip to content

Commit

Permalink
feat:(carousel): add dot-position prop (#366)
Browse files Browse the repository at this point in the history
* feat:n-input Support hidden password

* feat(form): support require-mark-placement(#171)

* Revert "feat(form): support require-mark-placement(#171)"

This reverts commit 0627777.

* Revert "feat:n-input Support hidden password"

This reverts commit ea64917.

* feat:(n-carousel): add dotPosition prop

* feat:(n-carousel): add test and fix code

* feat:(n-carousel): fix code

* feat:(n-carousel): fix code

* feat:(n-carousel): fix code and remove console
  • Loading branch information
doom-9-zz authored Jul 5, 2021
1 parent 4afc083 commit 85c0bae
Show file tree
Hide file tree
Showing 9 changed files with 207 additions and 35 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- `n-image` add `imgProps` prop.
- Add native `title` attributes to some components to enhance the experience.
- `n-tree` add `prefix` and `suffix` in TreeOption.
- `n-carousel` add `dot-placement` prop.

### Fixes

Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- `n-image` 增加 `imgProps` 属性
- 在部分组件上添加原生 `title` 属性,以提高用户体验
- `n-tree` 在 TreeOption 中新增 `prefix``suffix`
- `n-carousel` 增加 `dot-placement` 属性

### Fixes

Expand Down
32 changes: 32 additions & 0 deletions src/carousel/demos/enUS/dotPosition.demo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Set Dot Placement

Set `dot-placement` to change the indication point position.

```html
<n-carousel dot-placement="left" style="height: 240px;" autoplay>
<img
class="carousel-img"
src="https://s.anw.red/fav/1623979004.jpg!/fw/600/quality/77/ignore-error/true"
/>
<img
class="carousel-img"
src="https://s.anw.red/news/1623372884.jpg!/both/800x450/quality/78/progressive/true/ignore-error/true"
/>
<img
class="carousel-img"
src="https://s.anw.red/news/1623177220.jpg!/both/800x450/quality/78/progressive/true/ignore-error/true"
/>
<img
class="carousel-img"
src="https://s.anw.red/news/1623152423.jpg!/both/800x450/quality/78/progressive/true/ignore-error/true"
/>
</n-carousel>
```

```css
.carousel-img {
width: 100%;
height: 100%;
object-fit: cover;
}
```
2 changes: 2 additions & 0 deletions src/carousel/demos/enUS/index.demo-entry.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ It's usually used to display good news.
basic
autoplay
hover
dotPlacement
```

## API
Expand All @@ -18,6 +19,7 @@ hover
| --- | --- | --- | --- |
| autoplay | `boolean` | `false` | Whether to scroll automatically. |
| interval | `number` | `5000` | Auto play interval. |
| dot-placement | `'top' \| 'bottom' \| 'left' \| 'right'` | `'bottom'` | Dot placement in the panel. |
| trigger | `'click' \| 'hover'` | `'click'` | The way to trigger the switch. |

### Carousel Slots
Expand Down
32 changes: 32 additions & 0 deletions src/carousel/demos/zhCN/dotPosition.demo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# 更改指示点位置

设定 `dot-placement` 来更改指示点位置。

```html
<n-carousel dot-placement="left" style="height: 240px;" autoplay>
<img
class="carousel-img"
src="https://s.anw.red/fav/1623979004.jpg!/fw/600/quality/77/ignore-error/true"
/>
<img
class="carousel-img"
src="https://s.anw.red/news/1623372884.jpg!/both/800x450/quality/78/progressive/true/ignore-error/true"
/>
<img
class="carousel-img"
src="https://s.anw.red/news/1623177220.jpg!/both/800x450/quality/78/progressive/true/ignore-error/true"
/>
<img
class="carousel-img"
src="https://s.anw.red/news/1623152423.jpg!/both/800x450/quality/78/progressive/true/ignore-error/true"
/>
</n-carousel>
```

```css
.carousel-img {
width: 100%;
height: 100%;
object-fit: cover;
}
```
12 changes: 7 additions & 5 deletions src/carousel/demos/zhCN/index.demo-entry.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,19 @@
basic
autoplay
hover
dotPlacement
```

## API

### Carousel Props

| 名称 | 类型 | 默认值 | 说明 |
| -------- | -------------------- | --------- | -------------- |
| autoplay | `boolean` | `false` | 是否自动播放 |
| interval | `number` | `5000` | 自动播放的间隔 |
| trigger | `'click' \| 'hover'` | `'click'` | 触发切换的方式 |
| 名称 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| autoplay | `boolean` | `false` | 是否自动播放 |
| dot-placement | `'top' \| 'bottom' \| 'left' \| 'right'` | `'bottom'` | 轮播指示点位置 |
| interval | `number` | `5000` | 自动播放的间隔 |
| trigger | `'click' \| 'hover'` | `'click'` | 触发切换的方式 |

### Carousel Slots

Expand Down
100 changes: 74 additions & 26 deletions src/carousel/src/Carousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
h,
defineComponent,
ref,
toRef,
cloneVNode,
nextTick,
computed,
Expand All @@ -24,6 +25,10 @@ import style from './styles/index.cssr'
const carouselProps = {
...(useTheme.props as ThemeProps<CarouselTheme>),
autoplay: Boolean,
dotPlacement: {
type: String as PropType<'top' | 'bottom' | 'left' | 'right'>,
default: 'bottom'
},
interval: {
type: Number,
default: 5000
Expand All @@ -46,6 +51,7 @@ export default defineComponent({
const touchingRef = ref(false)
const dragOffsetRef = ref(0)
const selfElRef = ref<HTMLDivElement | null>(null)
const dotPlacementRef = toRef(props, 'dotPlacement')
let timerId: number | null = null
let inTransition = false
// current from 0 to length + 1
Expand Down Expand Up @@ -115,30 +121,52 @@ export default defineComponent({
}
}
let dragStartX = 0
let dragStartY = 0
let dragStartTime = 0
let memorizedContainerWidth = 0
let memorizedContainerHeight = 0
function handleTouchstart (e: TouchEvent): void {
if (timerId !== null) {
window.clearInterval(timerId)
}
e.preventDefault()
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
memorizedContainerWidth = selfElRef.value!.offsetWidth
touchingRef.value = true
dragStartTime = Date.now()
dragStartX = e.touches[0].clientX
const { value: dotPlacement } = dotPlacementRef
if (dotPlacement === 'left' || dotPlacement === 'right') {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
memorizedContainerHeight = selfElRef.value!.offsetHeight
touchingRef.value = true
dragStartTime = Date.now()
dragStartY = e.touches[0].clientY
} else {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
memorizedContainerWidth = selfElRef.value!.offsetWidth
touchingRef.value = true
dragStartTime = Date.now()
dragStartX = e.touches[0].clientX
}
on('touchmove', document, handleTouchmove)
on('touchend', document, handleTouchend)
on('touchcancel', document, handleTouchend)
}
function handleTouchmove (e: TouchEvent): void {
const dragOffset = e.touches[0].clientX - dragStartX
dragOffsetRef.value =
dragOffset > memorizedContainerWidth
? memorizedContainerWidth
: dragOffset < -memorizedContainerWidth
? -memorizedContainerWidth
: dragOffset
const { value: dotPlacement } = dotPlacementRef
if (dotPlacement === 'left' || dotPlacement === 'right') {
const dragOffset = e.touches[0].clientY - dragStartY
dragOffsetRef.value =
dragOffset > memorizedContainerHeight
? memorizedContainerHeight
: dragOffset < -memorizedContainerHeight
? -memorizedContainerHeight
: dragOffset
} else {
const dragOffset = e.touches[0].clientX - dragStartX
dragOffsetRef.value =
dragOffset > memorizedContainerWidth
? memorizedContainerWidth
: dragOffset < -memorizedContainerWidth
? -memorizedContainerWidth
: dragOffset
}
}
function handleTouchend (): void {
if (props.autoplay) resetInterval()
Expand All @@ -147,17 +175,29 @@ export default defineComponent({
})
const { value: selfEl } = selfElRef
if (selfEl) {
const { offsetWidth } = selfEl
const { offsetWidth, offsetHeight } = selfEl
const { value: dragOffset } = dragOffsetRef
const duration = Date.now() - dragStartTime
const { value: dotPlacement } = dotPlacementRef
// more than 50% width or faster than 0.4px per ms
if (dragOffset > offsetWidth / 2 || dragOffset / duration > 0.4) {
prev()
} else if (
dragOffset < -offsetWidth / 2 ||
dragOffset / duration < -0.4
) {
next()
if (dotPlacement === 'left' || dotPlacement === 'right') {
if (dragOffset > offsetHeight / 2 || dragOffset / duration > 0.4) {
prev()
} else if (
dragOffset < -offsetHeight / 2 ||
dragOffset / duration < -0.4
) {
next()
}
} else {
if (dragOffset > offsetWidth / 2 || dragOffset / duration > 0.4) {
prev()
} else if (
dragOffset < -offsetWidth / 2 ||
dragOffset / duration < -0.4
) {
next()
}
}
}
dragOffsetRef.value = 0
Expand Down Expand Up @@ -231,6 +271,7 @@ export default defineComponent({
},
render () {
const {
dotPlacement,
mergedClsPrefix,
current,
lengthRef,
Expand All @@ -242,21 +283,28 @@ export default defineComponent({
const leftOverflowVNode = length ? cloneVNode(children[length - 1]) : null
const rightOverflowVNode = length ? cloneVNode(children[0]) : null
const total = length + 2
const vertical = dotPlacement === 'left' || dotPlacement === 'right'
return (
<div
class={`${mergedClsPrefix}-carousel`}
class={[
`${mergedClsPrefix}-carousel`,
`${mergedClsPrefix}-carousel--${this.dotPlacement}`
]}
style={this.cssVars as CSSProperties}
ref="selfElRef"
>
<div
class={`${mergedClsPrefix}-carousel__slides`}
onTouchstart={this.handleTouchstart}
style={{
width: `${total}00%`,
[vertical ? 'height' : 'width']: `${total}00%`,
[vertical ? 'width' : 'height']: '100%',
transition: this.touching ? 'none' : '',
transform:
`translate3d(-${(100 / total) * (current % total)}%, 0, 0)` +
(this.touching ? `translateX(${this.dragOffset}px)` : '')
transform: vertical
? `translate3d(0, -${(100 / total) * (current % total)}%, 0)` +
(this.touching ? `translateY(${this.dragOffset}px)` : '')
: `translate3d(-${(100 / total) * (current % total)}%, 0, 0)` +
(this.touching ? `translateX(${this.dragOffset}px)` : '')
}}
onTransitionend={this.handleTransitionEnd}
role="listbox"
Expand All @@ -265,7 +313,7 @@ export default defineComponent({
(vNode, i) => (
<div
data-index={i}
style={{ width: `${100 / total}%` }}
style={{ [vertical ? 'height' : 'width']: `${100 / total}%` }}
key={i}
role="option"
aria-hidden={i !== current}
Expand Down
52 changes: 48 additions & 4 deletions src/carousel/src/styles/index.cssr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,10 @@ export default cB('carousel', `
]),
cE('dots', `
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: 16px;
display: flex;
flex-wrap: nowrap;
`),
cE('dot', `
margin-right: 12px;
height: var(--dot-size);
width: var(--dot-size);
background-color: var(--dot-color);
Expand All @@ -50,5 +46,53 @@ export default cB('carousel', `
c('&:last-child', `
margin-right: 0;
`)
]),
cM('left', [
cE('slides', `
flex-direction: column;
`),
cE('dots', `
transform: translateY(-50%);
top: 50%;
left: 16px;
flex-direction: column;
`),
cE('dot', `
margin-bottom: 12px;
`)
]),
cM('right', [
cE('slides', `
flex-direction: column;
`),
cE('dots', `
transform: translateY(-50%);
top: 50%;
right: 16px;
flex-direction: column;
`),
cE('dot', `
margin-bottom: 12px;
`)
]),
cM('top', [
cE('dots', `
transform: translateX(-50%);
top: 16px;
left: 50%;
`),
cE('dot', `
margin-right: 12px;
`)
]),
cM('bottom', [
cE('dots', `
transform: translateX(-50%);
bottom: 16px;
left: 50%;
`),
cE('dot', `
margin-right: 12px;
`)
])
])
10 changes: 10 additions & 0 deletions src/carousel/tests/Carousel.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,14 @@ describe('n-carousel', () => {
it('should work with import on demand', () => {
mount(NCarousel)
})
it('should work with `dotPlacement` prop', async () => {
const wrapper = mount(NCarousel)

for (const placement of ['top', 'bottom', 'left', 'right'] as const) {
await wrapper.setProps({ dotPlacement: placement })
expect(wrapper.find('.n-carousel').classes()).toContain(
`n-carousel--${placement}`
)
}
})
})

0 comments on commit 85c0bae

Please sign in to comment.