From 83ada1a2ba142aeea2df20c8b9ae00e36c71e5a3 Mon Sep 17 00:00:00 2001 From: LaamGinghong <351390560@qq.com> Date: Mon, 28 Dec 2020 21:04:09 +0800 Subject: [PATCH] feat(comp: space): add component space --- packages/components/components.less | 2 +- packages/components/core/config/types.ts | 7 +- .../components/core/config/useGlobalConfig.ts | 5 +- packages/components/core/types/index.ts | 2 + .../components/core/utils/getSlotNodeList.ts | 13 ++ packages/components/core/utils/index.ts | 1 + packages/components/index.ts | 4 +- .../__snapshots__/space.spec.ts.snap | 3 + .../components/space/__tests__/space.spec.ts | 185 ++++++++++++++++++ packages/components/space/demo/CustomSize.vue | 24 +++ packages/components/space/demo/Size.vue | 26 +++ packages/components/space/demo/Wrap.vue | 15 ++ packages/components/space/demo/basic.md | 23 +++ packages/components/space/demo/customSize.md | 10 + packages/components/space/demo/direction.md | 24 +++ packages/components/space/demo/size.md | 12 ++ packages/components/space/demo/split.md | 22 +++ packages/components/space/demo/wrap.md | 10 + packages/components/space/docs/index.zh.md | 42 ++++ packages/components/space/index.ts | 7 + packages/components/space/src/Space.vue | 98 ++++++++++ packages/components/space/src/types.ts | 26 +++ packages/components/space/style/index.less | 20 ++ packages/components/space/style/mixins.less | 70 +++++++ 24 files changed, 647 insertions(+), 4 deletions(-) create mode 100644 packages/components/core/utils/getSlotNodeList.ts create mode 100644 packages/components/space/__tests__/__snapshots__/space.spec.ts.snap create mode 100644 packages/components/space/__tests__/space.spec.ts create mode 100644 packages/components/space/demo/CustomSize.vue create mode 100644 packages/components/space/demo/Size.vue create mode 100644 packages/components/space/demo/Wrap.vue create mode 100644 packages/components/space/demo/basic.md create mode 100644 packages/components/space/demo/customSize.md create mode 100644 packages/components/space/demo/direction.md create mode 100644 packages/components/space/demo/size.md create mode 100644 packages/components/space/demo/split.md create mode 100644 packages/components/space/demo/wrap.md create mode 100644 packages/components/space/docs/index.zh.md create mode 100644 packages/components/space/index.ts create mode 100644 packages/components/space/src/Space.vue create mode 100644 packages/components/space/src/types.ts create mode 100644 packages/components/space/style/index.less create mode 100644 packages/components/space/style/mixins.less diff --git a/packages/components/components.less b/packages/components/components.less index 1c6a206f8..049a6b75a 100644 --- a/packages/components/components.less +++ b/packages/components/components.less @@ -3,4 +3,4 @@ @import './badge/style/index.less'; @import './divider/style/index.less'; @import './image/style/index.less'; - +@import './space/style/index.less'; diff --git a/packages/components/core/config/types.ts b/packages/components/core/config/types.ts index baeb2291e..287adcac6 100644 --- a/packages/components/core/config/types.ts +++ b/packages/components/core/config/types.ts @@ -1,4 +1,4 @@ -import type { ComponentSize, ButtonMode, DividerPosition, DividerType } from '../types' +import type { ComponentSize, ButtonMode, DividerPosition, DividerType, SpaceSize } from '../types' export type GlobalConfigKey = keyof GlobalConfig @@ -8,6 +8,7 @@ export interface GlobalConfig { badge: BadgeConfig divider: DividerConfig image: ImageConfig + space: SpaceConfig } export interface ButtonConfig { @@ -36,3 +37,7 @@ export interface ImageConfig { height: string | number fallback: string } + +export interface SpaceConfig { + size: SpaceSize +} diff --git a/packages/components/core/config/useGlobalConfig.ts b/packages/components/core/config/useGlobalConfig.ts index 37de7c661..3e51ad13c 100644 --- a/packages/components/core/config/useGlobalConfig.ts +++ b/packages/components/core/config/useGlobalConfig.ts @@ -8,6 +8,7 @@ import type { IconConfig, DividerConfig, ImageConfig, + SpaceConfig, } from './types' const button = shallowReactive({ mode: 'default', size: 'medium' }) @@ -19,13 +20,13 @@ const divider = shallowReactive({ position: 'center', type: 'horizontal', }) - const image: ImageConfig = shallowReactive({ width: 100, height: 100, fallback: 'data:image/png;base64,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', }) +const space = shallowReactive({ size: 'small' }) const defaultConfig: GlobalConfig = { button, @@ -33,6 +34,7 @@ const defaultConfig: GlobalConfig = { badge, divider, image, + space, } const globalConfigToken: Record = { @@ -41,6 +43,7 @@ const globalConfigToken: Record = { badge: Symbol(), divider: Symbol(), image: Symbol(), + space: Symbol(), } /** diff --git a/packages/components/core/types/index.ts b/packages/components/core/types/index.ts index b3a028374..677897450 100644 --- a/packages/components/core/types/index.ts +++ b/packages/components/core/types/index.ts @@ -5,3 +5,5 @@ export type ButtonMode = 'primary' | 'default' | 'dashed' | 'text' | 'link' export type DividerPosition = 'left' | 'center' | 'right' export type DividerType = 'horizontal' | 'vertical' + +export type SpaceSize = 'small' | 'medium' | 'large' | number diff --git a/packages/components/core/utils/getSlotNodeList.ts b/packages/components/core/utils/getSlotNodeList.ts new file mode 100644 index 000000000..d5cbf79c0 --- /dev/null +++ b/packages/components/core/utils/getSlotNodeList.ts @@ -0,0 +1,13 @@ +import type { Slots, VNode } from 'vue' + +interface GetSlotNodeList { + (slots: Slots, key?: string, options?: unknown[]): VNode[] +} + +export const getSlotNodeList: GetSlotNodeList = (slots, key = 'default', options = []) => { + if (!slots[key]) return [] + + const currentSlot = slots[key]!(options) + if (currentSlot.length === 1 && currentSlot[0].dynamicChildren) return currentSlot[0].dynamicChildren + return currentSlot +} diff --git a/packages/components/core/utils/index.ts b/packages/components/core/utils/index.ts index ed661eff3..6d30369db 100644 --- a/packages/components/core/utils/index.ts +++ b/packages/components/core/utils/index.ts @@ -1,2 +1,3 @@ export * from './installComponent' export * from './isDevMode' +export * from './getSlotNodeList' diff --git a/packages/components/index.ts b/packages/components/index.ts index d2af78517..c6b6a64c8 100644 --- a/packages/components/index.ts +++ b/packages/components/index.ts @@ -4,8 +4,9 @@ import { IxIcon } from './icon' import { IxBadge } from './badge' import { IxDivider } from './divider' import { IxImage } from './image' +import { IxSpace } from './space' -const components = [IxButton, IxButtonGroup, IxIcon, IxBadge, IxDivider, IxImage] +const components = [IxButton, IxButtonGroup, IxIcon, IxBadge, IxDivider, IxImage, IxSpace] const install = (app: App): void => { components.forEach(component => { @@ -28,3 +29,4 @@ export * from './icon' export * from './badge' export * from './divider' export * from './image' +export * from './space' diff --git a/packages/components/space/__tests__/__snapshots__/space.spec.ts.snap b/packages/components/space/__tests__/__snapshots__/space.spec.ts.snap new file mode 100644 index 000000000..5f482a3d3 --- /dev/null +++ b/packages/components/space/__tests__/__snapshots__/space.spec.ts.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Space.vue render work 1`] = `"
Space
"`; diff --git a/packages/components/space/__tests__/space.spec.ts b/packages/components/space/__tests__/space.spec.ts new file mode 100644 index 000000000..732249fd2 --- /dev/null +++ b/packages/components/space/__tests__/space.spec.ts @@ -0,0 +1,185 @@ +import { mount } from '@vue/test-utils' +import { defineComponent, PropType } from 'vue' +import IxSpace from '../src/Space.vue' +import { IxButton } from '../../button' +import { IxDivider } from '../../divider' +import { SpaceAlign, SpaceDirection } from '../src/types' +import { SpaceSize } from '@idux/components' +import { isNil } from '@idux/cdk/utils' + +const TestComponent = defineComponent({ + components: { IxSpace, IxButton, IxDivider }, + template: ` + + Space + Button + Button + + + `, + props: { + align: { type: String as PropType, default: undefined }, + direction: { type: String as PropType, default: undefined }, + size: { type: [String, Number, Array] as PropType, default: undefined }, + split: { type: String, default: undefined }, + wrap: { type: Boolean, default: undefined }, + }, + data() { + return { + showSplit: false, + } + }, + computed: { + dividerType(): SpaceDirection { + const hashmap = { + horizontal: 'vertical', + vertical: 'horizontal', + } + return hashmap[this.direction] as SpaceDirection + }, + }, +}) + +describe('Space.vue', () => { + test('render work', () => { + const wrapper = mount(TestComponent) + expect(wrapper.classes()).toContain('ix-space') + expect(wrapper.html()).toMatchSnapshot() + }) + + test('align work', async () => { + const wrapper = mount(TestComponent) + expect(wrapper.classes()).not.toContain('ix-space-start') + expect(wrapper.classes()).not.toContain('ix-space-center') + expect(wrapper.classes()).not.toContain('ix-space-end') + expect(wrapper.classes()).toContain('ix-space-baseline') + + await wrapper.setProps({ align: 'start' }) + expect(wrapper.classes()).toContain('ix-space-start') + expect(wrapper.classes()).not.toContain('ix-space-center') + expect(wrapper.classes()).not.toContain('ix-space-end') + expect(wrapper.classes()).not.toContain('ix-space-baseline') + + await wrapper.setProps({ align: 'center' }) + expect(wrapper.classes()).not.toContain('ix-space-start') + expect(wrapper.classes()).toContain('ix-space-center') + expect(wrapper.classes()).not.toContain('ix-space-end') + expect(wrapper.classes()).not.toContain('ix-space-baseline') + + await wrapper.setProps({ align: 'end' }) + expect(wrapper.classes()).not.toContain('ix-space-start') + expect(wrapper.classes()).not.toContain('ix-space-center') + expect(wrapper.classes()).toContain('ix-space-end') + expect(wrapper.classes()).not.toContain('ix-space-baseline') + + await wrapper.setProps({ align: 'baseline' }) + expect(wrapper.classes()).not.toContain('ix-space-start') + expect(wrapper.classes()).not.toContain('ix-space-center') + expect(wrapper.classes()).not.toContain('ix-space-end') + expect(wrapper.classes()).toContain('ix-space-baseline') + }) + + test('direction work', async () => { + const wrapper = mount(TestComponent) + expect(wrapper.classes()).toContain('ix-space-horizontal') + expect(wrapper.classes()).not.toContain('ix-space-vertical') + + await wrapper.setProps({ direction: 'vertical' }) + expect(wrapper.classes()).not.toContain('ix-space-horizontal') + expect(wrapper.classes()).toContain('ix-space-vertical') + }) + + test('wrap work', async () => { + const wrapper = mount(TestComponent) + expect(wrapper.classes()).not.toContain('ix-space-wrap') + + await wrapper.setProps({ wrap: true }) + expect(wrapper.classes()).toContain('ix-space-wrap') + }) + + test('split work', async () => { + const wrapper = mount(TestComponent) + expect(wrapper.find('.ix-divider').exists()).toBeFalsy() + expect(wrapper.find('.ix-space-split').exists()).toBeFalsy() + + await wrapper.setData({ showSplit: true }) + expect(wrapper.find('.ix-divider').exists()).toBeTruthy() + expect(wrapper.find('.ix-space-split').exists()).toBeFalsy() + + await wrapper.setProps({ split: '/' }) + expect(wrapper.find('.ix-divider').exists()).toBeTruthy() + expect(wrapper.find('.ix-space-split').exists()).toBeFalsy() + + await wrapper.setData({ showSplit: false }) + expect(wrapper.find('.ix-divider').exists()).toBeFalsy() + expect(wrapper.find('.ix-space-split').exists()).toBeTruthy() + }) + + test('size work', async () => { + const wrapper = mount(TestComponent) + expect(wrapper.findAll('.ix-space-item-small').length).toEqual(3) + expect(wrapper.findAll('.ix-space-item-medium').length).toEqual(0) + expect(wrapper.findAll('.ix-space-item-large').length).toEqual(0) + + await wrapper.setProps({ size: 'small' }) + expect(wrapper.findAll('.ix-space-item-small').length).toEqual(3) + expect(wrapper.findAll('.ix-space-item-medium').length).toEqual(0) + expect(wrapper.findAll('.ix-space-item-large').length).toEqual(0) + + await wrapper.setProps({ size: 'medium' }) + expect(wrapper.findAll('.ix-space-item-small').length).toEqual(0) + expect(wrapper.findAll('.ix-space-item-medium').length).toEqual(3) + expect(wrapper.findAll('.ix-space-item-large').length).toEqual(0) + + await wrapper.setProps({ size: 'large' }) + expect(wrapper.findAll('.ix-space-item-small').length).toEqual(0) + expect(wrapper.findAll('.ix-space-item-medium').length).toEqual(0) + expect(wrapper.findAll('.ix-space-item-large').length).toEqual(3) + + await wrapper.setProps({ size: 20 }) + expect(wrapper.findAll('.ix-space-item-small').length).toEqual(0) + expect(wrapper.findAll('.ix-space-item-medium').length).toEqual(0) + expect(wrapper.findAll('.ix-space-item-large').length).toEqual(0) + expect( + wrapper.findAll('.ix-space-item').every(item => item.attributes('style') === 'margin-right: 20px;'), + ).toBeTruthy() + + await wrapper.setProps({ split: '/' }) + expect(wrapper.findAll('.ix-space-item-small').length).toEqual(0) + expect(wrapper.findAll('.ix-space-item-medium').length).toEqual(0) + expect(wrapper.findAll('.ix-space-item-large').length).toEqual(0) + expect(wrapper.findAll('.ix-space-item').every(item => isNil(item.attributes('style')))).toBeTruthy() + + await wrapper.setProps({ split: undefined, size: ['small', 'medium'] }) + expect(wrapper.findAll('.ix-space-item-small').length).toEqual(1) + expect(wrapper.findAll('.ix-space-item-medium').length).toEqual(1) + expect(wrapper.findAll('.ix-space-item-large').length).toEqual(0) + + const size = new Map([ + [0, 20], + [1, 10], + ]) + await wrapper.setProps({ size: Array.from(size.values()) }) + expect(wrapper.findAll('.ix-space-item-small').length).toEqual(0) + expect(wrapper.findAll('.ix-space-item-medium').length).toEqual(0) + expect(wrapper.findAll('.ix-space-item-large').length).toEqual(0) + expect( + wrapper.findAll('.ix-space-item').reduce((pre, cur, index) => { + return pre && cur.attributes('style') === `margin-right: ${size.get(index)}px;` + }, true), + ) + + await wrapper.setProps({ split: '/' }) + expect(wrapper.findAll('.ix-space-item-small').length).toEqual(0) + expect(wrapper.findAll('.ix-space-item-medium').length).toEqual(0) + expect(wrapper.findAll('.ix-space-item-large').length).toEqual(0) + expect(wrapper.findAll('.ix-space-item').every(item => isNil(item.attributes('style')))).toBeTruthy() + + size.set(2, 30) + const warn = jest.spyOn(console, 'warn').mockImplementation(() => {}) + await wrapper.setProps({ split: undefined, size: Array.from(size.values()) }) + expect(warn).toBeCalled() + }) +}) diff --git a/packages/components/space/demo/CustomSize.vue b/packages/components/space/demo/CustomSize.vue new file mode 100644 index 000000000..cac071dbe --- /dev/null +++ b/packages/components/space/demo/CustomSize.vue @@ -0,0 +1,24 @@ + + + diff --git a/packages/components/space/demo/Size.vue b/packages/components/space/demo/Size.vue new file mode 100644 index 000000000..306c11554 --- /dev/null +++ b/packages/components/space/demo/Size.vue @@ -0,0 +1,26 @@ + + + diff --git a/packages/components/space/demo/Wrap.vue b/packages/components/space/demo/Wrap.vue new file mode 100644 index 000000000..53a567d69 --- /dev/null +++ b/packages/components/space/demo/Wrap.vue @@ -0,0 +1,15 @@ + + + diff --git a/packages/components/space/demo/basic.md b/packages/components/space/demo/basic.md new file mode 100644 index 000000000..7bd9884cd --- /dev/null +++ b/packages/components/space/demo/basic.md @@ -0,0 +1,23 @@ +--- +order: 0 +title: + zh: 基本用法 + en: Basic usage +--- + +## zh + +相邻组件水平间距。 + +## demo + +```html + +``` diff --git a/packages/components/space/demo/customSize.md b/packages/components/space/demo/customSize.md new file mode 100644 index 000000000..18993b8e9 --- /dev/null +++ b/packages/components/space/demo/customSize.md @@ -0,0 +1,10 @@ +--- +order: 4 +title: + zh: 自定义间距 + en: Custom space size +--- + +## zh + +自定义间距大小。 diff --git a/packages/components/space/demo/direction.md b/packages/components/space/demo/direction.md new file mode 100644 index 000000000..c192f98a6 --- /dev/null +++ b/packages/components/space/demo/direction.md @@ -0,0 +1,24 @@ +--- +order: 1 +title: + zh: 间距方向 + en: Space direction +--- + +## zh + +相邻组件垂直间距。 + +可以设置 `width: 100%` 独占一行。 + +## demo + +```html + + +``` diff --git a/packages/components/space/demo/size.md b/packages/components/space/demo/size.md new file mode 100644 index 000000000..399ea5b52 --- /dev/null +++ b/packages/components/space/demo/size.md @@ -0,0 +1,12 @@ +--- +order: 3 +title: + zh: 间距大小 + en: Space size +--- + +## zh + +间距预设大、中、小三种大小。 + +通过设置 `size` 为 `large` `medium` 分别把间距设为大、中间距。若不设置 `size` 或设置为 `small`,则间距为小。 diff --git a/packages/components/space/demo/split.md b/packages/components/space/demo/split.md new file mode 100644 index 000000000..dc6508e58 --- /dev/null +++ b/packages/components/space/demo/split.md @@ -0,0 +1,22 @@ +--- +order: 6 +title: + zh: 分隔符 + en: Divider +--- + +## zh + +相邻组件分隔符。 + +## demo + +```html + +``` diff --git a/packages/components/space/demo/wrap.md b/packages/components/space/demo/wrap.md new file mode 100644 index 000000000..ee02ad09d --- /dev/null +++ b/packages/components/space/demo/wrap.md @@ -0,0 +1,10 @@ +--- +order: 5 +title: + zh: 换行 + en: Wrap +--- + +## zh + +自动换行 diff --git a/packages/components/space/docs/index.zh.md b/packages/components/space/docs/index.zh.md new file mode 100644 index 000000000..dd70f5f49 --- /dev/null +++ b/packages/components/space/docs/index.zh.md @@ -0,0 +1,42 @@ +--- +category: components +type: 通用 +title: Space +subtitle: 间距 +cover: +--- + +设置组件之间的间距。 + +## 何时使用 + +避免组件紧贴在一起,拉开统一的空间。 + +- 适合行内元素的水平间距。 + +- 可以设置各种水平对齐方式。 + +## API + +### `ix-space` + +#### props + +| 属性 | 说明 | 类型 | 全局配置 | 默认值 | 备注 | +| --- | --- | --- | --- | --- | --- | +| `align` | 对齐方式 | `start \| center \| end \| baseline` | - | `baseline` | - | +| `direction` | 间距方向 | `vertical \| horizontal` | - | `horizontal` | - | +| `size` | 间距大小 | `SpaceSize \| SpaceSize[]` | `small` | - | 有三个预设间距大小,除此之外还可以传入一个数字自定义间距大小,还可以传入一个数组来控制每个间距的大小(数组长度需等于间距个数,否则组件会抛出警告) | +| `split` | 设置拆分 | string | - | - | 设置间隔分割符,当传入 split 时,内部间距会被分隔符替代;除了这种方式,你还可以设置 `v-slot: split` 的方式设置分隔符,优先级高于 prop | +| `wrap` | 是否换行 | boolean | - | false | 仅在 `horizontal` 时生效 | + +```typescript +type SpaceSize = 'small' | 'medium' | 'large' | number +``` + +#### slots + +| 属性 | 说明 | +| --------- | ---------------- | +| `default` | 需要被间隔的内容 | +| `split` | 分隔符 | diff --git a/packages/components/space/index.ts b/packages/components/space/index.ts new file mode 100644 index 000000000..df6d1874d --- /dev/null +++ b/packages/components/space/index.ts @@ -0,0 +1,7 @@ +import { installComponent } from '@idux/components/core/utils' +import IxSpace from './src/Space.vue' + +IxSpace.install = installComponent(IxSpace) + +export { IxSpace } +export type { IxSpaceComponent } from './src/types' diff --git a/packages/components/space/src/Space.vue b/packages/components/space/src/Space.vue new file mode 100644 index 000000000..92f1dea45 --- /dev/null +++ b/packages/components/space/src/Space.vue @@ -0,0 +1,98 @@ + + + diff --git a/packages/components/space/src/types.ts b/packages/components/space/src/types.ts new file mode 100644 index 000000000..f63dd3279 --- /dev/null +++ b/packages/components/space/src/types.ts @@ -0,0 +1,26 @@ +import type { SpaceSize } from '@idux/components/core/types' + +export type SpaceAlign = 'start' | 'center' | 'end' | 'baseline' +export type SpaceDirection = 'vertical' | 'horizontal' + +export interface SpaceProps { + /* Alignment direction of container */ + align?: SpaceAlign + /* Spacing direction of flex item */ + direction?: SpaceDirection + /** + * Spacing size. + * You can also pass in an array to customize the size of each spacing。 + */ + size?: SpaceSize | SpaceSize[] + /** + * Delimiter. + * You can also pass in a slot to customize the delimiter. + */ + split?: string + /* Whether to wrap */ + wrap?: boolean +} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface IxSpaceComponent extends SpaceProps {} diff --git a/packages/components/space/style/index.less b/packages/components/space/style/index.less new file mode 100644 index 000000000..84d073537 --- /dev/null +++ b/packages/components/space/style/index.less @@ -0,0 +1,20 @@ +@import '../../style/default.less'; +@import "./mixins.less"; + +@space-prefix: ~'@{idux-prefix}-space'; +@space-item-prefix: ~'@{space-prefix}-item'; + +.@{space-prefix} { + display: inline-flex; + + &-wrap { + flex-wrap: wrap; + + .@{space-item-prefix} { + margin-bottom: @margin-md; + } + } +} + +.space-align-traverse(@space-prefix); +.space-direction-traverse(@space-prefix); diff --git a/packages/components/space/style/mixins.less b/packages/components/space/style/mixins.less new file mode 100644 index 000000000..c13a83d50 --- /dev/null +++ b/packages/components/space/style/mixins.less @@ -0,0 +1,70 @@ +@import '../../style/default.less'; + +/* stylelint-disable property-no-unknown */ +@space-align: { + start: flex-start; + center: center; + end: flex-end; + baseline: baseline; +} + +/* stylelint-enable property-no-unknown */ + +.space-align-traverse(@prefix) { + each(@space-align, .(@value, @key) { + @space-item: ~'@{prefix}-@{key}'; + + .@{space-item} { + align-items: @value; + } + }) +} + +/* stylelint-disable property-no-unknown */ +@space-direction: { + horizontal: row; + vertical: column; +} + +/* stylelint-enable property-no-unknown */ + +.space-direction-traverse(@prefix) { + each(@space-direction, .(@value, @key) { + @space-item: ~'@{prefix}-@{key}'; + + .@{space-item} { + flex-direction: @value; + + .@{prefix}-split { + margin: if(@key=horizontal, 0 @margin-sm, @margin-sm 0); + } + + .space-size-traverse(@prefix, @key); + } + }) +} + +/* stylelint-disable property-no-unknown */ +@space-size: { + small: @margin-sm; + medium: @margin-md; + large: @margin-lg; +} + +/* stylelint-enable property-no-unknown */ + +.space-size-traverse(@prefix, @direction) { + each(@space-size, .(@value, @key) { + @space-item: ~'@{prefix}-item-@{key}'; + + .@{space-item} { + margin-right: if(@direction=horizontal, @value); + margin-bottom: if(@direction=vertical, @value); + + &:last-child{ + margin: 0; + } + } + }) +} +