From e671e22ea5d1dfb5e3bfbfa23528bd7666f53f5b Mon Sep 17 00:00:00 2001 From: chenjiahan Date: Sun, 5 Jul 2020 16:00:20 +0800 Subject: [PATCH] feat: layout component --- src-next/col/README.md | 119 +++++++++++++++++ src-next/col/README.zh-CN.md | 124 ++++++++++++++++++ src-next/col/demo/index.vue | 112 ++++++++++++++++ src-next/col/index.js | 51 +++++++ src-next/col/index.less | 20 +++ .../col/test/__snapshots__/demo.spec.js.snap | 58 ++++++++ .../col/test/__snapshots__/index.spec.js.snap | 22 ++++ src-next/col/test/demo.spec.js | 4 + src-next/col/test/index.spec.js | 48 +++++++ src-next/row/index.js | 87 ++++++++++++ src-next/utils/vnodes.ts | 47 +++---- vant.config.js | 16 +-- 12 files changed, 677 insertions(+), 31 deletions(-) create mode 100644 src-next/col/README.md create mode 100644 src-next/col/README.zh-CN.md create mode 100644 src-next/col/demo/index.vue create mode 100644 src-next/col/index.js create mode 100644 src-next/col/index.less create mode 100644 src-next/col/test/__snapshots__/demo.spec.js.snap create mode 100644 src-next/col/test/__snapshots__/index.spec.js.snap create mode 100644 src-next/col/test/demo.spec.js create mode 100644 src-next/col/test/index.spec.js create mode 100644 src-next/row/index.js diff --git a/src-next/col/README.md b/src-next/col/README.md new file mode 100644 index 000000000..d6be11217 --- /dev/null +++ b/src-next/col/README.md @@ -0,0 +1,119 @@ +# Layout + +### Intro + +Quickly and easily create layouts with `van-row` and `van-col` + +### Install + +```js +import Vue from 'vue'; +import { Col, Row } from 'vant'; + +Vue.use(Col); +Vue.use(Row); +``` + +## Usage + +### Basic Usage + +Layout are based on 24-column. The attribute `span` in `Col` means the number of column the grid spans. Of course, You can use `offset` attribute to set number of spacing on the left side of the grid. + +```html + + span: 8 + span: 8 + span: 8 + + + + span: 4 + offset: 4, span: 10 + span: 6 + + + + offset: 12, span: 12 + +``` + +### Column Spacing + +Set grid spacing using `gutter` attribute. The default value is 0 + +```html + + span: 8 + span: 8 + span: 8 + +``` + +### Flex Layout + +Setting `type` to `flex` to enable flex layout + +```html + + span: 6 + span: 6 + span: 6 + + + + span: 6 + span: 6 + span: 6 + + + + span: 6 + span: 6 + span: 6 + + + + span: 6 + span: 6 + span: 6 + + + + span: 6 + span: 6 + span: 6 + +``` + +## API + +### Row Props + +| Attribute | Description | Type | Default | +| --- | --- | --- | --- | +| type | Layout type, can be set to `flex` | _string_ | - | +| gutter | Grid spacing(px) | _number \| string_ | - | +| tag | Custom element tag | _string_ | `div` | +| justify | Flex main axis,can be set to end/center/space-around/space-between | _string_ | `start` | +| align | Flex cross axis, be set to center/bottom | _string_ | `top` | + +### Col Props + +| Attribute | Description | Type | Default | +| --- | --- | --- | --- | +| span | number of column the grid spans | _number \| string_ | - | +| offset | number of spacing on the left side of the grid | _number \| string_ | - | +| tag | Custom element tag | _string_ | `div` | + +### Row Events + +| Event | Description | Arguments | +| ----- | ------------------------ | -------------- | +| click | Triggered when click row | _event: Event_ | + +### Col Events + +| Event | Description | Arguments | +| ----- | ------------------------ | -------------- | +| click | Triggered when click col | _event: Event_ | diff --git a/src-next/col/README.zh-CN.md b/src-next/col/README.zh-CN.md new file mode 100644 index 000000000..7eb405993 --- /dev/null +++ b/src-next/col/README.zh-CN.md @@ -0,0 +1,124 @@ +# Layout 布局 + +### 介绍 + +Layout 提供了`van-row`和`van-col`两个组件来进行行列布局 + +### 引入 + +```js +import Vue from 'vue'; +import { Col, Row } from 'vant'; + +Vue.use(Col); +Vue.use(Row); +``` + +## 代码演示 + +### 基础用法 + +Layout 组件提供了`24列栅格`,通过在`Col`上添加`span`属性设置列所占的宽度百分比 +此外,添加`offset`属性可以设置列的偏移宽度,计算方式与 span 相同 + +```html + + span: 8 + span: 8 + span: 8 + + + + span: 4 + offset: 4, span: 10 + + + + offset: 12, span: 12 + +``` + +### 设置列元素间距 + +通过`gutter`属性可以设置列元素之间的间距,默认间距为 0 + +```html + + span: 8 + span: 8 + span: 8 + +``` + +### Flex 布局 + +将 `type` 属性设置为 flex 可以启用 flex 布局,便于进行灵活的对齐 + +```html + + + span: 6 + span: 6 + span: 6 + + + + + span: 6 + span: 6 + span: 6 + + + + + span: 6 + span: 6 + span: 6 + + + + + span: 6 + span: 6 + span: 6 + + + + + span: 6 + span: 6 + span: 6 + +``` + +## API + +### Row Props + +| 参数 | 说明 | 类型 | 默认值 | +| --- | --- | --- | --- | +| type | 布局方式,可选值为`flex` | _string_ | - | +| gutter | 列元素之间的间距(单位为 px) | _number \| string_ | - | +| tag | 自定义元素标签 | _string_ | `div` | +| justify | Flex 主轴对齐方式,可选值为 `end` `center`
`space-around` `space-between` | _string_ | `start` | +| align | Flex 交叉轴对齐方式,可选值为 `center` `bottom` | _string_ | `top` | + +### Col Props + +| 参数 | 说明 | 类型 | 默认值 | +| ------ | -------------- | ------------------ | ------ | +| span | 列元素宽度 | _number \| string_ | - | +| offset | 列元素偏移距离 | _number \| string_ | - | +| tag | 自定义元素标签 | _string_ | `div` | + +### Row Events + +| 事件名 | 说明 | 回调参数 | +| ------ | ---------- | -------------- | +| click | 点击时触发 | _event: Event_ | + +### Col Events + +| 事件名 | 说明 | 回调参数 | +| ------ | ---------- | -------------- | +| click | 点击时触发 | _event: Event_ | diff --git a/src-next/col/demo/index.vue b/src-next/col/demo/index.vue new file mode 100644 index 000000000..5b396922a --- /dev/null +++ b/src-next/col/demo/index.vue @@ -0,0 +1,112 @@ + + + + + diff --git a/src-next/col/index.js b/src-next/col/index.js new file mode 100644 index 000000000..99f0c3e23 --- /dev/null +++ b/src-next/col/index.js @@ -0,0 +1,51 @@ +import { createNamespace } from '../utils'; +import { ChildrenMixin } from '../mixins/relation'; + +const [createComponent, bem] = createNamespace('col'); + +export default createComponent({ + mixins: [ChildrenMixin('vanRow')], + + props: { + span: [Number, String], + offset: [Number, String], + tag: { + type: String, + default: 'div', + }, + }, + + computed: { + style() { + const { index } = this; + const { spaces } = this.parent || {}; + + if (spaces && spaces[index]) { + const { left, right } = spaces[index]; + return { + paddingLeft: left ? `${left}px` : null, + paddingRight: right ? `${right}px` : null, + }; + } + }, + }, + + methods: { + onClick(event) { + this.$emit('click', event); + }, + }, + + render() { + const { span, offset } = this; + return ( + + {this.$slots.default?.()} + + ); + }, +}); diff --git a/src-next/col/index.less b/src-next/col/index.less new file mode 100644 index 000000000..35fa2f4f5 --- /dev/null +++ b/src-next/col/index.less @@ -0,0 +1,20 @@ +@import '../style/var'; + +.van-col { + float: left; + box-sizing: border-box; + min-height: 1px; +} + +.generate-col(24); +.generate-col(@n, @i: 1) when (@i =< @n) { + .van-col--@{i} { + width: @i * 100% / 24; + } + + .van-col--offset-@{i} { + margin-left: @i * 100% / 24; + } + + .generate-col(@n, (@i + 1)); +} diff --git a/src-next/col/test/__snapshots__/demo.spec.js.snap b/src-next/col/test/__snapshots__/demo.spec.js.snap new file mode 100644 index 000000000..7c04f9b40 --- /dev/null +++ b/src-next/col/test/__snapshots__/demo.spec.js.snap @@ -0,0 +1,58 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders demo correctly 1`] = ` +
+
+
+
span: 8
+
span: 8
+
span: 8
+
+
+
span: 4
+
+ offset: 4, span: 10 +
+
+
+
+ offset: 12, span: 12 +
+
+
+
+
+
span: 8
+
span: 8
+
span: 8
+
+
+
+
+
span: 6
+
span: 6
+
span: 6
+
+
+
span: 6
+
span: 6
+
span: 6
+
+
+
span: 6
+
span: 6
+
span: 6
+
+
+
span: 6
+
span: 6
+
span: 6
+
+
+
span: 6
+
span: 6
+
span: 6
+
+
+
+`; diff --git a/src-next/col/test/__snapshots__/index.spec.js.snap b/src-next/col/test/__snapshots__/index.spec.js.snap new file mode 100644 index 000000000..2d4588095 --- /dev/null +++ b/src-next/col/test/__snapshots__/index.spec.js.snap @@ -0,0 +1,22 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`gutter prop 1`] = ` +
+
24
+
12
+
12
+
8
+
8
+
8
+
6
+
6
+
6
+
6
+
7
+
6
+
5
+
4
+
3
+
2
+
+`; diff --git a/src-next/col/test/demo.spec.js b/src-next/col/test/demo.spec.js new file mode 100644 index 000000000..5c70922b5 --- /dev/null +++ b/src-next/col/test/demo.spec.js @@ -0,0 +1,4 @@ +import Demo from '../demo'; +import { snapshotDemo } from '../../../test/demo'; + +snapshotDemo(Demo); diff --git a/src-next/col/test/index.spec.js b/src-next/col/test/index.spec.js new file mode 100644 index 000000000..dfe0a20ac --- /dev/null +++ b/src-next/col/test/index.spec.js @@ -0,0 +1,48 @@ +import Col from '..'; +import Row from '../../row'; +import { mount } from '../../../test'; + +test('Col click event', () => { + const wrapper = mount(Col); + wrapper.trigger('click'); + + expect(wrapper.emitted('click')).toBeTruthy(); +}); + +test('Row click event', () => { + const wrapper = mount(Row); + wrapper.trigger('click'); + + expect(wrapper.emitted('click')).toBeTruthy(); +}); + +test('gutter prop', () => { + const wrapper = mount({ + template: ` + + 24 + + 12 + 12 + + 8 + 8 + 8 + + 6 + 6 + 6 + 6 + + 7 + 6 + 5 + 4 + 3 + 2 + + `, + }); + + expect(wrapper).toMatchSnapshot(); +}); diff --git a/src-next/row/index.js b/src-next/row/index.js new file mode 100644 index 000000000..9500641c6 --- /dev/null +++ b/src-next/row/index.js @@ -0,0 +1,87 @@ +import { createNamespace } from '../utils'; +import { ParentMixin } from '../mixins/relation'; + +const [createComponent, bem] = createNamespace('row'); + +export default createComponent({ + mixins: [ParentMixin('vanRow')], + + props: { + type: String, + align: String, + justify: String, + tag: { + type: String, + default: 'div', + }, + gutter: { + type: [Number, String], + default: 0, + }, + }, + + computed: { + spaces() { + const gutter = Number(this.gutter); + + if (!gutter) { + return; + } + + const spaces = []; + const groups = [[]]; + + let totalSpan = 0; + this.children.forEach((item, index) => { + totalSpan += Number(item.span); + + if (totalSpan > 24) { + groups.push([index]); + totalSpan -= 24; + } else { + groups[groups.length - 1].push(index); + } + }); + + groups.forEach((group) => { + const averagePadding = (gutter * (group.length - 1)) / group.length; + + group.forEach((item, index) => { + if (index === 0) { + spaces.push({ right: averagePadding }); + } else { + const left = gutter - spaces[item - 1].right; + const right = averagePadding - left; + spaces.push({ left, right }); + } + }); + }); + + return spaces; + }, + }, + + methods: { + onClick(event) { + this.$emit('click', event); + }, + }, + + render() { + const { align, justify } = this; + const flex = this.type === 'flex'; + + return ( + + {this.$slots.default?.()} + + ); + }, +}); diff --git a/src-next/utils/vnodes.ts b/src-next/utils/vnodes.ts index 471d2ea38..34cc6bf37 100644 --- a/src-next/utils/vnodes.ts +++ b/src-next/utils/vnodes.ts @@ -1,33 +1,34 @@ -import { VNode } from 'vue'; +// import { VNode } from 'vue'; -function flattenVNodes(vnodes: VNode[]) { - const result: VNode[] = []; +// function flattenVNodes(vnodes: VNode[]) { +// const result: VNode[] = []; - function traverse(vnodes: VNode[]) { - vnodes.forEach((vnode) => { - result.push(vnode); +// function traverse(vnodes: VNode[]) { +// vnodes.forEach((vnode) => { +// result.push(vnode); - if (vnode.componentInstance) { - traverse(vnode.componentInstance.$children.map((item) => item.$vnode)); - } +// if (vnode.componentInstance) { +// traverse(vnode.componentInstance.$children.map((item) => item.$vnode)); +// } - if (vnode.children) { - traverse(vnode.children); - } - }); - } +// if (vnode.children) { +// traverse(vnode.children); +// } +// }); +// } - traverse(vnodes); - return result; -} +// traverse(vnodes); +// return result; +// } +// TODO // sort children instances by vnodes order export function sortChildren(children: Vue[], parent: Vue) { - const { componentOptions } = parent.$vnode; - if (!componentOptions || !componentOptions.children) { - return; - } + // const { componentOptions } = parent.$vnode; + // if (!componentOptions || !componentOptions.children) { + // return; + // } - const vnodes = flattenVNodes(componentOptions.children); - children.sort((a, b) => vnodes.indexOf(a.$vnode) - vnodes.indexOf(b.$vnode)); + // const vnodes = flattenVNodes(componentOptions.children); + // children.sort((a, b) => vnodes.indexOf(a.$vnode) - vnodes.indexOf(b.$vnode)); } diff --git a/vant.config.js b/vant.config.js index 9f64456b3..67ce5e06d 100644 --- a/vant.config.js +++ b/vant.config.js @@ -94,10 +94,10 @@ module.exports = { path: 'image', title: 'Image 图片', }, - // { - // path: 'col', - // title: 'Layout 布局', - // }, + { + path: 'col', + title: 'Layout 布局', + }, // { // path: 'popup', // title: 'Popup 弹出层', @@ -441,10 +441,10 @@ module.exports = { path: 'image', title: 'Image', }, - // { - // path: 'col', - // title: 'Layout', - // }, + { + path: 'col', + title: 'Layout', + }, // { // path: 'popup', // title: 'Popup',