From 6590e94de7b6a255dd140682d8548f3023e9e753 Mon Sep 17 00:00:00 2001 From: LaamGinghong <351390560@qq.com> Date: Sun, 20 Dec 2020 21:55:29 +0800 Subject: [PATCH] feat(comp: divider): add divider component --- packages/components/components.less | 1 + packages/components/core/config/types.ts | 10 +- .../components/core/config/useGlobalConfig.ts | 16 +- packages/components/core/types/index.ts | 4 + .../__snapshots__/divider.spec.ts.snap | 3 + .../divider/__tests__/divider.spec.ts | 196 ++++++++++++++++++ .../components/divider/demo/horizontal.md | 29 +++ packages/components/divider/demo/plain.md | 32 +++ packages/components/divider/demo/vertical.md | 22 ++ packages/components/divider/demo/with-text.md | 34 +++ packages/components/divider/docs/index.zh.md | 26 +++ packages/components/divider/index.ts | 7 + packages/components/divider/src/Divider.vue | 57 +++++ packages/components/divider/src/types.ts | 15 ++ packages/components/divider/style/index.less | 78 +++++++ packages/components/index.ts | 4 +- tsconfig.json | 3 - 17 files changed, 528 insertions(+), 9 deletions(-) create mode 100644 packages/components/divider/__tests__/__snapshots__/divider.spec.ts.snap create mode 100644 packages/components/divider/__tests__/divider.spec.ts create mode 100644 packages/components/divider/demo/horizontal.md create mode 100755 packages/components/divider/demo/plain.md create mode 100755 packages/components/divider/demo/vertical.md create mode 100755 packages/components/divider/demo/with-text.md create mode 100644 packages/components/divider/docs/index.zh.md create mode 100644 packages/components/divider/index.ts create mode 100644 packages/components/divider/src/Divider.vue create mode 100644 packages/components/divider/src/types.ts create mode 100644 packages/components/divider/style/index.less diff --git a/packages/components/components.less b/packages/components/components.less index 41338dcc7..bc8282c66 100644 --- a/packages/components/components.less +++ b/packages/components/components.less @@ -1,3 +1,4 @@ @import './button/style/index.less'; @import './icon/style/index.less'; @import './badge/style/index.less'; +@import './divider/style/index.less'; diff --git a/packages/components/core/config/types.ts b/packages/components/core/config/types.ts index bc90a15a4..d4cec6566 100644 --- a/packages/components/core/config/types.ts +++ b/packages/components/core/config/types.ts @@ -1,4 +1,4 @@ -import type { ComponentSize, ButtonMode } from '../types' +import type { ComponentSize, ButtonMode, DividerPosition, DividerType } from '../types' export type GlobalConfigKey = keyof GlobalConfig @@ -6,6 +6,7 @@ export interface GlobalConfig { button: ButtonConfig icon: IconConfig badge: BadgeConfig + divider: DividerConfig } export interface ButtonConfig { @@ -22,3 +23,10 @@ export interface BadgeConfig { dot: boolean overflowCount: number | string } + +export interface DividerConfig { + dashed: boolean + plain: boolean + position: DividerPosition + type: DividerType +} diff --git a/packages/components/core/config/useGlobalConfig.ts b/packages/components/core/config/useGlobalConfig.ts index 6360ad28b..ad4b417a6 100644 --- a/packages/components/core/config/useGlobalConfig.ts +++ b/packages/components/core/config/useGlobalConfig.ts @@ -1,21 +1,29 @@ import { inject, shallowReactive, provide, readonly } from 'vue' import type { DeepReadonly } from 'vue' -import type { BadgeConfig, ButtonConfig, GlobalConfig, GlobalConfigKey, IconConfig } from './types' +import type { BadgeConfig, ButtonConfig, GlobalConfig, GlobalConfigKey, IconConfig, DividerConfig } from './types' -const button: ButtonConfig = shallowReactive({ mode: 'default', size: 'medium' }) -const icon: IconConfig = shallowReactive({}) -const badge: BadgeConfig = shallowReactive({ showZero: false, dot: false, overflowCount: 99 }) +const button = shallowReactive({ mode: 'default', size: 'medium' }) +const icon = shallowReactive({}) +const badge = shallowReactive({ showZero: false, dot: false, overflowCount: 99 }) +const divider = shallowReactive({ + dashed: false, + plain: false, + position: 'center', + type: 'horizontal', +}) const defaultConfig: GlobalConfig = { button, icon, badge, + divider, } const globalConfigToken: Record = { button: Symbol(), icon: Symbol(), badge: Symbol(), + divider: Symbol(), } /** diff --git a/packages/components/core/types/index.ts b/packages/components/core/types/index.ts index b3dea1d08..b3a028374 100644 --- a/packages/components/core/types/index.ts +++ b/packages/components/core/types/index.ts @@ -1,3 +1,7 @@ export type ComponentSize = 'large' | 'medium' | 'small' export type ButtonMode = 'primary' | 'default' | 'dashed' | 'text' | 'link' + +export type DividerPosition = 'left' | 'center' | 'right' + +export type DividerType = 'horizontal' | 'vertical' diff --git a/packages/components/divider/__tests__/__snapshots__/divider.spec.ts.snap b/packages/components/divider/__tests__/__snapshots__/divider.spec.ts.snap new file mode 100644 index 000000000..43a3cd27a --- /dev/null +++ b/packages/components/divider/__tests__/__snapshots__/divider.spec.ts.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Divider.vue render work 1`] = `"
"`; diff --git a/packages/components/divider/__tests__/divider.spec.ts b/packages/components/divider/__tests__/divider.spec.ts new file mode 100644 index 000000000..1f4dfe8b2 --- /dev/null +++ b/packages/components/divider/__tests__/divider.spec.ts @@ -0,0 +1,196 @@ +import { mount, MountingOptions, VueWrapper } from '@vue/test-utils' +import { DefineComponent } from 'vue' +import IxDivider from '../src/Divider.vue' +import { DividerProps } from '../src/types' + +describe('Divider.vue', () => { + let DividerMount: ( + options?: MountingOptions>, + ) => VueWrapper>> + + beforeEach(() => { + DividerMount = (options = {}) => { + return mount(IxDivider, { + ...options, + }) + } + }) + + test('render work', () => { + const wrapper = DividerMount() + expect(wrapper.html()).toMatchSnapshot() + }) + + test('type work', async () => { + const wrapper = DividerMount() + expect(wrapper.classes()).toContain('ix-divider-horizontal') + + await wrapper.setProps({ type: 'horizontal' }) + expect(wrapper.classes()).toContain('ix-divider-horizontal') + + await wrapper.setProps({ type: 'vertical' }) + expect(wrapper.classes()).toContain('ix-divider-vertical') + }) + + test('dashed work', async () => { + const wrapper = DividerMount() + expect(wrapper.classes()).not.toContain('ix-divider-dashed') + + await wrapper.setProps({ dashed: true }) + expect(wrapper.classes()).toContain('ix-divider-dashed') + + await wrapper.setProps({ dashed: false }) + expect(wrapper.classes()).not.toContain('ix-divider-dashed') + }) + + test('slot work', async () => { + const text = 'Text' + let wrapper = DividerMount() + expect(wrapper.classes()).not.toContain('ix-divider-with-text') + expect(wrapper.text()).not.toEqual(text) + expect(wrapper.find('.ix-divider-inner-text').exists()).toBeFalsy() + + wrapper = DividerMount({ slots: { default: text } }) + expect(wrapper.classes()).toContain('ix-divider-with-text') + expect(wrapper.text()).toEqual(text) + expect(wrapper.find('.ix-divider-inner-text').exists()).toBeTruthy() + }) + + test('plain work', async () => { + const text = 'Text' + let wrapper = DividerMount() + expect(wrapper.classes()).not.toContain('ix-divider-plain') + + wrapper = DividerMount({ slots: { default: text } }) + expect(wrapper.classes()).not.toContain('ix-divider-plain') + + await wrapper.setProps({ plain: true }) + expect(wrapper.classes()).toContain('ix-divider-plain') + + await wrapper.setProps({ plain: false }) + expect(wrapper.classes()).not.toContain('ix-divider-plain') + }) + + test('position work', async () => { + const text = 'Text' + let wrapper = DividerMount() + expect(wrapper.classes()).not.toContain('ix-divider-with-text-left') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-center') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-right') + + await wrapper.setProps({ type: 'horizontal' }) + expect(wrapper.classes()).not.toContain('ix-divider-with-text-left') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-center') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-right') + + await wrapper.setProps({ type: 'vertical' }) + expect(wrapper.classes()).not.toContain('ix-divider-with-text-left') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-center') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-right') + + wrapper = DividerMount({ slots: { default: text } }) + expect(wrapper.classes()).not.toContain('ix-divider-with-text-left') + expect(wrapper.classes()).toContain('ix-divider-with-text-center') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-right') + + await wrapper.setProps({ type: 'horizontal' }) + expect(wrapper.classes()).not.toContain('ix-divider-with-text-left') + expect(wrapper.classes()).toContain('ix-divider-with-text-center') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-right') + + await wrapper.setProps({ type: 'vertical' }) + expect(wrapper.classes()).not.toContain('ix-divider-with-text-left') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-center') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-right') + + wrapper = DividerMount({ slots: { default: text }, props: { position: 'left' } }) + expect(wrapper.classes()).toContain('ix-divider-with-text-left') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-center') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-right') + + await wrapper.setProps({ type: 'horizontal' }) + expect(wrapper.classes()).toContain('ix-divider-with-text-left') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-center') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-right') + + await wrapper.setProps({ type: 'vertical' }) + expect(wrapper.classes()).not.toContain('ix-divider-with-text-left') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-center') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-right') + + wrapper = DividerMount({ props: { position: 'left' } }) + expect(wrapper.classes()).not.toContain('ix-divider-with-text-left') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-center') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-right') + + await wrapper.setProps({ type: 'horizontal' }) + expect(wrapper.classes()).not.toContain('ix-divider-with-text-left') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-center') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-right') + + await wrapper.setProps({ type: 'vertical' }) + expect(wrapper.classes()).not.toContain('ix-divider-with-text-left') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-center') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-right') + + wrapper = DividerMount({ slots: { default: text }, props: { position: 'center' } }) + expect(wrapper.classes()).not.toContain('ix-divider-with-text-left') + expect(wrapper.classes()).toContain('ix-divider-with-text-center') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-right') + + await wrapper.setProps({ type: 'horizontal' }) + expect(wrapper.classes()).not.toContain('ix-divider-with-text-left') + expect(wrapper.classes()).toContain('ix-divider-with-text-center') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-right') + + await wrapper.setProps({ type: 'vertical' }) + expect(wrapper.classes()).not.toContain('ix-divider-with-text-left') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-center') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-right') + + wrapper = DividerMount({ props: { position: 'center' } }) + expect(wrapper.classes()).not.toContain('ix-divider-with-text-left') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-center') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-right') + + await wrapper.setProps({ type: 'horizontal' }) + expect(wrapper.classes()).not.toContain('ix-divider-with-text-left') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-center') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-right') + + await wrapper.setProps({ type: 'vertical' }) + expect(wrapper.classes()).not.toContain('ix-divider-with-text-left') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-center') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-right') + + wrapper = DividerMount({ slots: { default: text }, props: { position: 'right' } }) + expect(wrapper.classes()).not.toContain('ix-divider-with-text-left') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-center') + expect(wrapper.classes()).toContain('ix-divider-with-text-right') + + await wrapper.setProps({ type: 'horizontal' }) + expect(wrapper.classes()).not.toContain('ix-divider-with-text-left') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-center') + expect(wrapper.classes()).toContain('ix-divider-with-text-right') + + await wrapper.setProps({ type: 'vertical' }) + expect(wrapper.classes()).not.toContain('ix-divider-with-text-left') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-center') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-right') + + wrapper = DividerMount({ props: { position: 'right' } }) + expect(wrapper.classes()).not.toContain('ix-divider-with-text-left') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-center') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-right') + + await wrapper.setProps({ type: 'horizontal' }) + expect(wrapper.classes()).not.toContain('ix-divider-with-text-left') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-center') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-right') + + await wrapper.setProps({ type: 'vertical' }) + expect(wrapper.classes()).not.toContain('ix-divider-with-text-left') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-center') + expect(wrapper.classes()).not.toContain('ix-divider-with-text-right') + }) +}) diff --git a/packages/components/divider/demo/horizontal.md b/packages/components/divider/demo/horizontal.md new file mode 100644 index 000000000..3809088e1 --- /dev/null +++ b/packages/components/divider/demo/horizontal.md @@ -0,0 +1,29 @@ +--- +order: 0 +title: + zh: 水平分割线 + en: Horizontal divider +--- + +## zh + +默认为水平分割线,可在中间加入文字。 + +## demo + +```html + + +``` diff --git a/packages/components/divider/demo/plain.md b/packages/components/divider/demo/plain.md new file mode 100755 index 000000000..cb6da4339 --- /dev/null +++ b/packages/components/divider/demo/plain.md @@ -0,0 +1,32 @@ +--- +order: 1 +title: + zh: 分割线使用正文样式 + en: divider text use plain style +--- + +## zh + +使用 `plain` 可以设置为更轻量的分割线文字样式。 + +## demo + +```html + +``` diff --git a/packages/components/divider/demo/vertical.md b/packages/components/divider/demo/vertical.md new file mode 100755 index 000000000..1ce2befee --- /dev/null +++ b/packages/components/divider/demo/vertical.md @@ -0,0 +1,22 @@ +--- +order: 3 +title: + zh: 垂直分割线 + en: vertical divider +--- + +## zh + +使用 `type="vertical"`设置为行内的垂直分割线 + +## demo + +```html + +``` diff --git a/packages/components/divider/demo/with-text.md b/packages/components/divider/demo/with-text.md new file mode 100755 index 000000000..a8056e27a --- /dev/null +++ b/packages/components/divider/demo/with-text.md @@ -0,0 +1,34 @@ +--- +order: 2 +title: + zh: 带文字的分割线 + en: divider with text +--- + +## zh + +分割线中带有文字,可以用 `position` 制定文字的位置。 + +文字默认位置为 `center`,除此之外你还可以设置为 `left` 和 `center`,同时你还可以通过全局配置修改默认位置。 + +## demo + +```html + +``` diff --git a/packages/components/divider/docs/index.zh.md b/packages/components/divider/docs/index.zh.md new file mode 100644 index 000000000..8fa110bdf --- /dev/null +++ b/packages/components/divider/docs/index.zh.md @@ -0,0 +1,26 @@ +--- +category: components +type: 通用 +title: Divider +subtitle: 分割线 +cover: +--- + +区隔内容的分割线 + +## 何时使用 + +* 对不同章节的文本段落进行分割 +* 对行内文字/链接进行分割,例如表格的操作列 + +## API + +### `ix-divider` + +| 属性 | 说明 | 类型 | 全局配置 | 默认值 | 备注 | +| --- | :-- | --- | :-- | --- | --- | +| `dashed` | 是否虚线 | boolean | false | - | - | +| `plain` | 文字是否显示为普通正文样式 | boolean |false| - | - | +| `position` | 文字显示位置 | `left`|`center`|`right` |`center`| - |-| +| `type` | 水平分割线还是垂直分割线 | `horizontal`|`vertical` | - |`horizontal`| +| `text` | 分割线显示文字 | `slot` |-| - | - | diff --git a/packages/components/divider/index.ts b/packages/components/divider/index.ts new file mode 100644 index 000000000..082d9b917 --- /dev/null +++ b/packages/components/divider/index.ts @@ -0,0 +1,7 @@ +import { installComponent } from '@idux/components/core/utils' +import IxDivider from './src/Divider.vue' + +IxDivider.install = installComponent(IxDivider) + +export { IxDivider } +export type { IxDividerComponent } from './src/types' diff --git a/packages/components/divider/src/Divider.vue b/packages/components/divider/src/Divider.vue new file mode 100644 index 000000000..c444d451f --- /dev/null +++ b/packages/components/divider/src/Divider.vue @@ -0,0 +1,57 @@ + + diff --git a/packages/components/divider/src/types.ts b/packages/components/divider/src/types.ts new file mode 100644 index 000000000..d96c7c2a4 --- /dev/null +++ b/packages/components/divider/src/types.ts @@ -0,0 +1,15 @@ +import type { DividerPosition, DividerType } from '@idux/components/core/types' + +export interface DividerProps { + /* dashed divider */ + dashed?: boolean + /* common text style */ + plain?: boolean + /* position of the text */ + position?: DividerPosition + /* horizontal divider or vertical divider */ + type?: DividerType +} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface IxDividerComponent extends DividerProps {} diff --git a/packages/components/divider/style/index.less b/packages/components/divider/style/index.less new file mode 100644 index 000000000..c5f3ab7e0 --- /dev/null +++ b/packages/components/divider/style/index.less @@ -0,0 +1,78 @@ +@import '../../style/default.less'; + +@divider-prefix: ~'@{idux-prefix}-divider'; + +.@{divider-prefix} { + border-top: @border-size-sm solid @border-color; + + &-horizontal { + width: 100%; + min-width: 100%; + margin: @margin-xl 0; + clear: both; + + &.@{divider-prefix}-with-text { + position: relative; + + .@{divider-prefix}-inner-text { + position: absolute; + display: inline-block; + padding: 0 1em; + font-size: @font-size-lg; + color: @black; + font-weight: @font-weight-md; + background: @white; + top: 50%; + transform: translateY(-50%); + } + + @divider-prefix-with-text: ~'@{divider-prefix}-with-text'; + + &.@{divider-prefix-with-text}-center { + .@{divider-prefix}-inner-text { + left: 50%; + transform: translate(-50%, -50%); + } + } + + &.@{divider-prefix-with-text}-left { + .@{divider-prefix}-inner-text { + left: 5%; + } + } + + &.@{divider-prefix-with-text}-right { + .@{divider-prefix}-inner-text { + right: 5%; + } + } + + &.@{divider-prefix}-plain { + .@{divider-prefix}-inner-text { + font-size: @font-size-md; + font-weight: @font-weight-sm; + } + } + } + } + + &-vertical { + top: -0.06em; + display: inline-block; + height: 0.9em; + vertical-align: middle; + border-top: 0; + border-left: @border-size-sm solid @border-color; + margin: 0 @margin-sm; + + &.@{divider-prefix}-dashed { + border-width: 0 0 0 1px; + } + } + + &-dashed { + border-style: dashed; + border-width: 1px 0 0; + background: none; + } +} diff --git a/packages/components/index.ts b/packages/components/index.ts index ee5e62b06..b92b75408 100644 --- a/packages/components/index.ts +++ b/packages/components/index.ts @@ -2,8 +2,9 @@ import type { App } from 'vue' import { IxButton, IxButtonGroup } from './button' import { IxIcon } from './icon' import { IxBadge } from './badge' +import { IxDivider } from './divider' -const components = [IxButton, IxButtonGroup, IxIcon, IxBadge] +const components = [IxButton, IxButtonGroup, IxIcon, IxBadge, IxDivider] const install = (app: App): void => { components.forEach(component => { @@ -25,3 +26,4 @@ export * from './button' export * from './i18n' export * from './icon' export * from './badge' +export * from './divider' diff --git a/tsconfig.json b/tsconfig.json index 1f4994962..6d11a1420 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,6 @@ "target": "ES6", "moduleResolution": "node", "jsx": "preserve", - "sourceMap": true, "declaration": true, "downlevelIteration": true, @@ -15,14 +14,12 @@ "esModuleInterop": true, "resolveJsonModule": true, "removeComments": true, - "strict": true, "noUnusedLocals": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "forceConsistentCasingInFileNames": true, "allowSyntheticDefaultImports": true, - "paths": { "@idux": ["./packages"], "@idux/*": ["./packages/*"]