diff --git a/components.json b/components.json
index da3f772d4..dd4b25daa 100644
--- a/components.json
+++ b/components.json
@@ -6,6 +6,7 @@
"cell": "./packages/cell/index.js",
"icon": "./packages/icon/index.js",
"cell-group": "./packages/cell-group/index.js",
+ "cell-swipe": "./packages/cell-swipe/index.js",
"popup": "./packages/popup/index.js",
"dialog": "./packages/dialog/index.js",
"picker": "./packages/picker/index.js",
diff --git a/docs/examples-dist/cell-swipe.vue b/docs/examples-dist/cell-swipe.vue
new file mode 100644
index 000000000..cd91bd534
--- /dev/null
+++ b/docs/examples-dist/cell-swipe.vue
@@ -0,0 +1,39 @@
+Cell Swipe 滑动单元格
+
+
+
+
+
+
+ 删除
+
+
+ 选择
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/examples-dist/search.vue b/docs/examples-dist/search.vue
index 24b981d97..0a05cfb6e 100644
--- a/docs/examples-dist/search.vue
+++ b/docs/examples-dist/search.vue
@@ -3,6 +3,9 @@
+
+
+
diff --git a/docs/examples-docs/cell-swipe.md b/docs/examples-docs/cell-swipe.md
new file mode 100644
index 000000000..51659b0be
--- /dev/null
+++ b/docs/examples-docs/cell-swipe.md
@@ -0,0 +1,91 @@
+
+
+## 滑动单元格
+
+### 使用指南
+
+如果你已经按照快速上手中引入了整个`vant`,以下**组件注册**就可以忽略了,因为你已经全局注册了`vant`中的全部组件。
+
+#### 全局注册
+
+你可以在全局注册`Cell Swipe`组件,比如页面的主文件(`index.js`,`main.js`),这样页面任何地方都可以直接使用`Cell Swipe`组件了:
+
+```js
+import Vue from 'vue';
+import { CellSwipe } from 'vant';
+import 'vant/lib/vant-css/cell-swipe.css';
+
+Vue.component(CellSwipe.name, CellSwipe);
+```
+
+#### 局部注册
+
+如果你只是想在某个组件中使用,你可以在对应组件中注册`Cell Swipe`组件,这样只能在你注册的组件中使用`Cell Swipe`:
+
+```js
+import { CellSwipe } from 'vant';
+
+export default {
+ components: {
+ 'van-cell-swipe': CellSwipe
+ }
+};
+```
+
+### 代码演示
+
+#### 基础用法
+
+:::demo 基础用法
+```html
+
+
+
+
+
+
+ 删除
+
+
+ 选择
+
+
+```
+:::
+
+
+### API
+
+| 参数 | 说明 | 类型 | 默认值 | 可选值 |
+|-----------|-----------|-----------|-------------|-------------|
+| right-width | 右侧滑动按钮宽度 | `number` | 0 | |
+| left-width | 左侧滑动按钮宽度 | `number` | 0 | |
+
+### Slot
+
+| name | 描述 |
+|-----------|-----------|
+| - | 自定义显示内容 |
+| right | 右侧滑动内容 |
+| left | 左侧滑动内容 |
diff --git a/docs/src/nav.config.js b/docs/src/nav.config.js
index b64adcc35..6f155b732 100644
--- a/docs/src/nav.config.js
+++ b/docs/src/nav.config.js
@@ -37,6 +37,10 @@ module.exports = {
"path": "/cell",
"title": "Cell 单元格"
},
+ {
+ "path": "/cell-swipe",
+ "title": "Cell Swipe 滑动单元格"
+ },
{
"path": "/progress",
"title": "Progress 进度条"
diff --git a/packages/cell-swipe/components/CellSwipe.vue b/packages/cell-swipe/components/CellSwipe.vue
new file mode 100644
index 000000000..777402261
--- /dev/null
+++ b/packages/cell-swipe/components/CellSwipe.vue
@@ -0,0 +1,142 @@
+
+
+
+
+
+
diff --git a/packages/cell-swipe/index.js b/packages/cell-swipe/index.js
new file mode 100644
index 000000000..bb9559c21
--- /dev/null
+++ b/packages/cell-swipe/index.js
@@ -0,0 +1,2 @@
+import CellSwipe from './components/CellSwipe.vue'
+export default CellSwipe;
diff --git a/packages/vant-css/src/cell-swipe.css b/packages/vant-css/src/cell-swipe.css
new file mode 100644
index 000000000..637e93854
--- /dev/null
+++ b/packages/vant-css/src/cell-swipe.css
@@ -0,0 +1,27 @@
+
+.van-cell-swipe .van-cell-wrapper, .van-cell-swipe .van-cell-left, .van-cell-swipe .van-cell-right {
+ -webkit-transition: -webkit-transform 150ms ease-in-out;
+ transition: -webkit-transform 150ms ease-in-out;
+ transition: transform 150ms ease-in-out;
+ transition: transform 150ms ease-in-out, -webkit-transform 150ms ease-in-out;
+}
+
+.van-cell-swipe{
+ position: relative;
+ min-height: 48px;
+ overflow: hidden;
+}
+.van-cell-right{
+ position: absolute;
+ height: 100%;
+ right: 0;
+ top: 0;
+ transform: translate3d(100%,0,0);
+}
+.van-cell-left {
+ position: absolute;
+ height: 100%;
+ left: 0;
+ top: 0;
+ transform: translate3d(-100%,0,0);
+}
\ No newline at end of file
diff --git a/packages/vant-css/src/index.css b/packages/vant-css/src/index.css
index 85785cd78..d83ee1789 100644
--- a/packages/vant-css/src/index.css
+++ b/packages/vant-css/src/index.css
@@ -4,6 +4,7 @@
@import './reset.css';
@import './button.css';
@import './cell.css';
+@import './cell-swipe.css';
@import './card.css';
@import './dialog.css';
@import './field.css';
diff --git a/src/index.js b/src/index.js
index 9566cdfb8..37c200d8c 100644
--- a/src/index.js
+++ b/src/index.js
@@ -5,6 +5,7 @@ import Radio from '../packages/radio/index.js';
import Cell from '../packages/cell/index.js';
import Icon from '../packages/icon/index.js';
import CellGroup from '../packages/cell-group/index.js';
+import CellSwipe from '../packages/cell-swipe/index.js';
import Popup from '../packages/popup/index.js';
import Dialog from '../packages/dialog/index.js';
import Picker from '../packages/picker/index.js';
@@ -47,6 +48,7 @@ const install = function(Vue) {
Vue.component(Cell.name, Cell);
Vue.component(Icon.name, Icon);
Vue.component(CellGroup.name, CellGroup);
+ Vue.component(CellSwipe.name, CellSwipe);
Vue.component(Popup.name, Popup);
Vue.component(Picker.name, Picker);
Vue.component(RadioGroup.name, RadioGroup);
@@ -89,6 +91,7 @@ module.exports = {
Cell,
Icon,
CellGroup,
+ CellSwipe,
Popup,
Dialog,
Picker,
diff --git a/src/utils/dom.js b/src/utils/dom.js
index ede0951ec..b5c2550a6 100644
--- a/src/utils/dom.js
+++ b/src/utils/dom.js
@@ -55,3 +55,44 @@ export function removeClass(el, cls) {
el.className = trim(curClass);
}
};
+export const once = function(el, event, fn) {
+ var listener = function() {
+ if (fn) {
+ fn.apply(this, arguments);
+ }
+ off(el, event, listener);
+ };
+ on(el, event, listener);
+};
+
+export const on = (function() {
+ if (document.addEventListener) {
+ return function(element, event, handler) {
+ if (element && event && handler) {
+ element.addEventListener(event, handler, false);
+ }
+ };
+ } else {
+ return function(element, event, handler) {
+ if (element && event && handler) {
+ element.attachEvent('on' + event, handler);
+ }
+ };
+ }
+})();
+
+export const off = (function() {
+ if (document.removeEventListener) {
+ return function(element, event, handler) {
+ if (element && event) {
+ element.removeEventListener(event, handler, false);
+ }
+ };
+ } else {
+ return function(element, event, handler) {
+ if (element && event) {
+ element.detachEvent('on' + event, handler);
+ }
+ };
+ }
+})();
diff --git a/test/unit/specs/cell-swipe.spec.js b/test/unit/specs/cell-swipe.spec.js
new file mode 100644
index 000000000..46325f291
--- /dev/null
+++ b/test/unit/specs/cell-swipe.spec.js
@@ -0,0 +1,141 @@
+import CellSwipe from 'packages/cell-swipe';
+import { mount } from 'avoriaz';
+
+describe('CellSwipe', () => {
+ let wrapper;
+ afterEach(() => {
+ wrapper && wrapper.destroy();
+ });
+
+ it('create a CellSwipe', () => {
+ wrapper = mount(CellSwipe, {
+ propsData: {
+ leftWidth: 2,
+ rightWidth: 2
+ }
+ });
+ wrapper.vm.startDrag({
+ pageX: 0,
+ pageY: 0
+ });
+ wrapper.vm.onDrag({
+ preventDefault() {},
+ pageY: 0,
+ pageX: 50
+ });
+ wrapper.vm.offsetLeft = -20;
+ wrapper.vm.rightWidth = 10;
+ wrapper.vm.swipeLeaveTransition(1);
+ wrapper.vm.endDrag();
+ expect(wrapper.hasClass('van-cell-swipe')).to.be.true;
+ });
+});
+
+
+describe('CellSwipe-left', () => {
+ let wrapper;
+ afterEach(() => {
+ wrapper && wrapper.destroy();
+ });
+
+ it('create a CellSwipe left', () => {
+ wrapper = mount(CellSwipe, {
+ propsData: {
+ leftWidth: 2,
+ rightWidth: 2
+ }
+ });
+ wrapper.vm.startDrag({
+ changedTouches: [{
+ pageX: 0,
+ pageY: 0
+ }
+ ]
+ });
+ wrapper.vm.onDrag({
+ preventDefault() {},
+ changedTouches: [{
+ pageX: 0,
+ pageY: -50
+ }
+ ]
+ });
+ wrapper.vm.offsetLeft = 20;
+ wrapper.vm.rightWidth = 10;
+ wrapper.vm.swipeLeaveTransition(-1);
+ wrapper.vm.endDrag();
+ expect(wrapper.hasClass('van-cell-swipe')).to.be.true;
+ });
+});
+
+describe('CellSwipe-0', () => {
+ let wrapper;
+ afterEach(() => {
+ wrapper && wrapper.destroy();
+ });
+
+ it('create a CellSwipe 0', () => {
+ wrapper = mount(CellSwipe, {
+ propsData: {
+ leftWidth: 0,
+ rightWidth: 2
+ }
+ });
+ wrapper.vm.startDrag({
+ pageX: 0,
+ pageY: 0
+ });
+ wrapper.vm.onDrag({
+ preventDefault() {},
+ pageY: 0,
+ pageX: -2
+ });
+ wrapper.vm.opened = true;
+ wrapper.vm.onDrag({
+ preventDefault() {},
+ pageY: 0,
+ pageX: -2
+ });
+ wrapper.vm.opened = false;
+ wrapper.vm.onDrag({
+ preventDefault() {},
+ pageY: 0,
+ pageX: 40
+ });
+ wrapper.vm.swipeLeaveTransition(0);
+ wrapper.vm.endDrag();
+ expect(wrapper.hasClass('van-cell-swipe')).to.be.true;
+ });
+});
+
+
+describe('CellSwipe-0', () => {
+ let wrapper;
+ afterEach(() => {
+ wrapper && wrapper.destroy();
+ });
+
+ it('create a CellSwipe 0', () => {
+ wrapper = mount(CellSwipe, {
+ propsData: {
+ leftWidth: 0,
+ rightWidth: 2
+ }
+ });
+ wrapper.vm.startDrag({
+ pageX: 0,
+ pageY: 0
+ });
+ wrapper.vm.onDrag({
+ preventDefault() {},
+ pageY: 1000,
+ pageX: 40
+ });
+ wrapper.vm.swipeMove();
+ wrapper.vm.swiping = false;
+ wrapper.vm.endDrag();
+ wrapper.vm.swiping = true;
+ wrapper.vm.endDrag();
+ expect(wrapper.hasClass('van-cell-swipe')).to.be.true;
+ });
+});