From 6e1cca769d5495575fd44642c6007ce80cc6289f Mon Sep 17 00:00:00 2001 From: 07akioni <07akioni2@gmail.com> Date: Sat, 3 Apr 2021 11:29:37 +0800 Subject: [PATCH] feat(page-header) --- CHANGELOG.en-US.md | 9 ++ CHANGELOG.zh-CN.md | 9 ++ demo/routes/routes.js | 10 ++ demo/store/menu-options.js | 6 + src/_internal/icons/ArrowBack.tsx | 13 ++ src/_internal/icons/ArrowDown.tsx | 8 +- src/_internal/icons/index.ts | 1 + src/components.ts | 1 + src/config-provider/src/internal-interface.ts | 2 + src/page-header/demos/enUS/basic.demo.md | 87 +++++++++++++ .../demos/enUS/index.demo-entry.md | 30 +++++ src/page-header/demos/zhCN/basic.demo.md | 84 ++++++++++++ .../demos/zhCN/index.demo-entry.md | 30 +++++ src/page-header/index.ts | 1 + src/page-header/src/PageHeader.tsx | 123 ++++++++++++++++++ src/page-header/src/styles/index.cssr.ts | 66 ++++++++++ src/page-header/styles/_common.ts | 4 + src/page-header/styles/dark.ts | 29 +++++ src/page-header/styles/index.ts | 3 + src/page-header/styles/light.ts | 35 +++++ 20 files changed, 544 insertions(+), 7 deletions(-) create mode 100644 src/_internal/icons/ArrowBack.tsx create mode 100644 src/page-header/demos/enUS/basic.demo.md create mode 100644 src/page-header/demos/enUS/index.demo-entry.md create mode 100644 src/page-header/demos/zhCN/basic.demo.md create mode 100644 src/page-header/demos/zhCN/index.demo-entry.md create mode 100644 src/page-header/index.ts create mode 100644 src/page-header/src/PageHeader.tsx create mode 100644 src/page-header/src/styles/index.cssr.ts create mode 100644 src/page-header/styles/_common.ts create mode 100644 src/page-header/styles/dark.ts create mode 100644 src/page-header/styles/index.ts create mode 100644 src/page-header/styles/light.ts diff --git a/CHANGELOG.en-US.md b/CHANGELOG.en-US.md index f9bfd81a2..82f3ccc10 100644 --- a/CHANGELOG.en-US.md +++ b/CHANGELOG.en-US.md @@ -2,6 +2,15 @@ ## Pending +### Feats + +- Add `n-page-header` component. +- `n-statistic` add label slot. + +### Refactors + +- Refactor `n-statistic`'s style + ### Fixes - Fix `n-collapse` content is truncated by `overflow: hidden`. diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md index 8491a3806..2e6192da8 100644 --- a/CHANGELOG.zh-CN.md +++ b/CHANGELOG.zh-CN.md @@ -2,6 +2,15 @@ ## Pending +### Feats + +- 添加 `n-page-header` 组件 +- `n-statistic` 增加 label slot + +### Refactors + +- 重构 `n-statistic` 样式 + ### Fixes - 修正 `n-collapse` 内容被 `overflow: hidden` 截断 diff --git a/demo/routes/routes.js b/demo/routes/routes.js index cfc230e97..8587b2423 100644 --- a/demo/routes/routes.js +++ b/demo/routes/routes.js @@ -371,6 +371,11 @@ export const enComponentRoutes = [ path: 'n-mention', component: () => import('../../src/mention/demos/enUS/index.demo-entry.md') }, + { + path: 'n-page-header', + component: () => + import('../../src/page-header/demos/enUS/index.demo-entry.md') + }, // deprecated { path: 'n-nimbus-service-layout', @@ -682,6 +687,11 @@ export const zhComponentRoutes = [ path: 'n-mention', component: () => import('../../src/mention/demos/zhCN/index.demo-entry.md') }, + { + path: 'n-page-header', + component: () => + import('../../src/page-header/demos/zhCN/index.demo-entry.md') + }, // deprecated { path: 'n-nimbus-service-layout', diff --git a/demo/store/menu-options.js b/demo/store/menu-options.js index 763ec90f6..74faedad2 100644 --- a/demo/store/menu-options.js +++ b/demo/store/menu-options.js @@ -175,6 +175,12 @@ export function createComponentMenuOptions ({ lang, theme, mode }) { enSuffix: true, path: '/n-icon' }, + { + en: 'PageHeader', + zh: '页头', + enSuffix: true, + path: '/n-page-header' + }, { en: 'Tag', zh: '标签', diff --git a/src/_internal/icons/ArrowBack.tsx b/src/_internal/icons/ArrowBack.tsx new file mode 100644 index 000000000..1bf4dd785 --- /dev/null +++ b/src/_internal/icons/ArrowBack.tsx @@ -0,0 +1,13 @@ +import { h, defineComponent } from 'vue' + +export default defineComponent({ + name: 'ArrowBack', + render () { + return ( + + + + + ) + } +}) diff --git a/src/_internal/icons/ArrowDown.tsx b/src/_internal/icons/ArrowDown.tsx index e25f53b37..2560b6683 100644 --- a/src/_internal/icons/ArrowDown.tsx +++ b/src/_internal/icons/ArrowDown.tsx @@ -4,13 +4,7 @@ export default defineComponent({ name: 'ArrowDown', render () { return ( - + diff --git a/src/_internal/icons/index.ts b/src/_internal/icons/index.ts index 89c3ea4e0..e1e4e8b91 100644 --- a/src/_internal/icons/index.ts +++ b/src/_internal/icons/index.ts @@ -29,3 +29,4 @@ export { default as ClearIcon } from './Clear' export { default as ChevronDownFilledIcon } from './ChevronDownFilled' export { default as ToIcon } from './To' export { default as RetryIcon } from './Retry' +export { default as ArrowBackIcon } from './ArrowBack' diff --git a/src/components.ts b/src/components.ts index 01a16c97e..fbe748de8 100644 --- a/src/components.ts +++ b/src/components.ts @@ -41,6 +41,7 @@ export * from './mention' export * from './message' export * from './modal' export * from './notification' +export * from './page-header' export * from './pagination' export * from './popconfirm' export * from './popover' diff --git a/src/config-provider/src/internal-interface.ts b/src/config-provider/src/internal-interface.ts index 592c24a7a..8b1019e23 100644 --- a/src/config-provider/src/internal-interface.ts +++ b/src/config-provider/src/internal-interface.ts @@ -38,6 +38,7 @@ import type { MentionTheme } from '../../mention/styles' import type { MessageTheme } from '../../message/styles' import type { ModalTheme } from '../../modal/styles' import type { NotificationTheme } from '../../notification/styles' +import type { PageHeaderTheme } from '../../page-header/styles' import type { PaginationTheme } from '../../pagination/styles' import type { PopconfirmTheme } from '../../popconfirm/styles' import type { PopoverTheme } from '../../popover/styles' @@ -117,6 +118,7 @@ export interface GlobalThemeWithoutCommon { Message?: MessageTheme Modal?: ModalTheme Notification?: NotificationTheme + PageHeader?: PageHeaderTheme Pagination?: PaginationTheme Popconfirm?: PopconfirmTheme Popover?: PopoverTheme diff --git a/src/page-header/demos/enUS/basic.demo.md b/src/page-header/demos/enUS/basic.demo.md new file mode 100644 index 000000000..550d0b1ce --- /dev/null +++ b/src/page-header/demos/enUS/basic.demo.md @@ -0,0 +1,87 @@ +# Basic + +```html + + + + + + + + + + + + + + + + + + + + + + + + +``` + +```js +import { defineComponent } from 'vue' +import { useMessage } from 'naive-ui' +import { EllipsisHorizontal } from '@vicons/ionicons5' + +export default defineComponent({ + components: { + EllipsisHorizontal + }, + setup () { + const message = useMessage() + return { + handleBack () { + message.info('[onBack]') + }, + options: [ + { + label: 'Urge Update', + key: '1' + }, + { + label: 'Urge Update', + key: '2' + }, + { + label: 'Urge Update', + key: '3' + } + ] + } + } +}) +``` diff --git a/src/page-header/demos/enUS/index.demo-entry.md b/src/page-header/demos/enUS/index.demo-entry.md new file mode 100644 index 000000000..3845d9a3a --- /dev/null +++ b/src/page-header/demos/enUS/index.demo-entry.md @@ -0,0 +1,30 @@ + + +# PageHeader + +## Demos + +```demo +basic +``` + +## Props + +| Name | Type | Default | Description | +| -------- | ------------ | ----------- | ----------- | +| extra | `string` | `undefined` | | +| subtitle | `string` | `undefined` | | +| title | `string` | `undefined` | | +| on-back | `() => void` | `undefined` | | + +## Slots + +| Name | Parameters | Description | +| -------- | ---------- | ----------- | +| avatar | `()` | | +| header | `()` | | +| default | `()` | | +| extra | `()` | | +| footer | `()` | | +| subtitle | `()` | | +| title | `()` | | diff --git a/src/page-header/demos/zhCN/basic.demo.md b/src/page-header/demos/zhCN/basic.demo.md new file mode 100644 index 000000000..1361a6219 --- /dev/null +++ b/src/page-header/demos/zhCN/basic.demo.md @@ -0,0 +1,84 @@ +# 基础用法 + +```html + + + + + + + + + + + + + + + + + + + + + + + + +``` + +```js +import { defineComponent } from 'vue' +import { useMessage } from 'naive-ui' +import { EllipsisHorizontal } from '@vicons/ionicons5' + +export default defineComponent({ + components: { + EllipsisHorizontal + }, + setup () { + const message = useMessage() + return { + handleBack () { + message.info('[onBack]') + }, + options: [ + { + label: '催更', + key: '1' + }, + { + label: '催更', + key: '2' + }, + { + label: '催更', + key: '3' + } + ] + } + } +}) +``` diff --git a/src/page-header/demos/zhCN/index.demo-entry.md b/src/page-header/demos/zhCN/index.demo-entry.md new file mode 100644 index 000000000..1e82b416d --- /dev/null +++ b/src/page-header/demos/zhCN/index.demo-entry.md @@ -0,0 +1,30 @@ + + +# 页头 PageHeader + +## 演示 + +```demo +basic +``` + +## Props + +| 名称 | 类型 | 默认值 | 说明 | +| -------- | ------------ | ----------- | ---- | +| extra | `string` | `undefined` | | +| subtitle | `string` | `undefined` | | +| title | `string` | `undefined` | | +| on-back | `() => void` | `undefined` | | + +## Slots + +| 名称 | 参数 | 说明 | +| -------- | ---- | ---- | +| avatar | `()` | | +| header | `()` | | +| default | `()` | | +| extra | `()` | | +| footer | `()` | | +| subtitle | `()` | | +| title | `()` | | diff --git a/src/page-header/index.ts b/src/page-header/index.ts new file mode 100644 index 000000000..bb0f671cd --- /dev/null +++ b/src/page-header/index.ts @@ -0,0 +1 @@ +export { default as NPageHeader } from './src/PageHeader' diff --git a/src/page-header/src/PageHeader.tsx b/src/page-header/src/PageHeader.tsx new file mode 100644 index 000000000..1628f6c7f --- /dev/null +++ b/src/page-header/src/PageHeader.tsx @@ -0,0 +1,123 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import { h, defineComponent, computed, CSSProperties, PropType } from 'vue' +import { useTheme } from '../../_mixins' +import type { ThemeProps } from '../../_mixins' +import { pageHeaderLight } from '../styles/light' +import type { PageHeaderTheme } from '../styles/light' +import style from './styles/index.cssr' +import { ArrowBackIcon } from '../../_internal/icons' +import { NBaseIcon } from '../../_internal' + +export default defineComponent({ + name: 'PageHeader', + props: { + ...(useTheme.props as ThemeProps), + title: String, + subtitle: String, + extra: String, + onBack: Function as PropType<() => void> + }, + setup (props) { + const themeRef = useTheme( + 'PageHeader', + 'PageHeader', + style, + pageHeaderLight, + props + ) + return { + cssVars: computed(() => { + const { + self: { + titleTextColor, + subtitleTextColor, + backColor, + fontSize, + titleFontSize, + backSize, + titleFontWeight, + backColorHover, + backColorPressed + }, + common: { cubicBezierEaseInOut } + } = themeRef.value + return { + '--title-text-color': titleTextColor, + '--title-font-size': titleFontSize, + '--title-font-weight': titleFontWeight, + '--font-size': fontSize, + '--back-size': backSize, + '--subtitle-text-color': subtitleTextColor, + '--back-color': backColor, + '--back-color-hover': backColorHover, + '--back-color-pressed': backColorPressed, + '--bezier': cubicBezierEaseInOut + } + }) + } + }, + render () { + const { onBack, title, subtitle, extra, $slots } = this + const { + title: titleSlot, + subtitle: subtitleSlot, + extra: extraSlot, + default: defaultSlot, + header: headerSlot, + avatar: avatarSlot, + footer: footerSlot + } = $slots + const showBack = onBack + const showTitle = title || titleSlot + const showSubtitle = subtitle || subtitleSlot + const showExtra = extra || extraSlot + return ( +
+ {headerSlot ? ( +
+ {headerSlot()} +
+ ) : null} +
+
+ {showBack ? ( +
+ + {{ + default: () => + }} + +
+ ) : null} + {avatarSlot ? ( +
{avatarSlot()}
+ ) : null} + {showTitle ? ( +
+ {title || titleSlot!()} +
+ ) : null} + {showSubtitle ? ( +
+ {subtitle || subtitleSlot!()} +
+ ) : null} +
+ {showExtra ? ( +
{extra || extraSlot!()}
+ ) : null} +
+ {defaultSlot ? ( +
+ {defaultSlot()} +
+ ) : null} + {footerSlot ? ( + + ) : null} +
+ ) + } +}) diff --git a/src/page-header/src/styles/index.cssr.ts b/src/page-header/src/styles/index.cssr.ts new file mode 100644 index 000000000..30c47ee4a --- /dev/null +++ b/src/page-header/src/styles/index.cssr.ts @@ -0,0 +1,66 @@ +import { c, cB, cE } from '../../../_utils/cssr' + +// vars: +// --title-font-size +// --title-text-color +// --font-size +// --subtitle-text-color +// --back-color +// --back-color-hover +// --back-color-pressed +// --back-size +// --title-font-weight +// --bezier +export default c([ + cB('page-header-header', ` + margin-bottom: 20px; + `), + cB('page-header', ` + display: flex; + align-items: center; + justify-content: space-between; + line-height: 1.5; + font-size: var(--font-size); + `, [ + cE('main', ` + display: flex; + flex-wrap: nowrap; + align-items: center; + `), + cE('back', ` + display: flex; + margin-right: 16px; + font-size: var(--back-size); + cursor: pointer; + color: var(--back-color); + transition: color .3s var(--bezier); + `, [ + c('&:hover', 'color: var(--back-color-hover);'), + c('&:active', 'color: var(--back-color-pressed);') + ]), + cE('avatar', ` + display: flex; + margin-right: 12px + `), + cE('title', ` + margin-right: 16px; + transition: color .3s var(--bezier); + font-size: var(--title-font-size); + font-weight: var(--title-font-weight); + color: var(--title-text-color); + `), + cE('subtitle', ` + font-size: 14px; + transition: color .3s var(--bezier); + color: var(--subtitle-text-color); + `) + ]), + cB('page-header-content', ` + margin-top: 20px; + font-size: var(--font-size); + `), + cB('page-header-footer', ` + margin-top: 20px; + font-size: var(--font-size); + `) +]) diff --git a/src/page-header/styles/_common.ts b/src/page-header/styles/_common.ts new file mode 100644 index 000000000..34147238c --- /dev/null +++ b/src/page-header/styles/_common.ts @@ -0,0 +1,4 @@ +export default { + titleFontSize: '18px', + backSize: '22px' +} diff --git a/src/page-header/styles/dark.ts b/src/page-header/styles/dark.ts new file mode 100644 index 000000000..832555727 --- /dev/null +++ b/src/page-header/styles/dark.ts @@ -0,0 +1,29 @@ +import type { PageHeaderTheme } from './light' +import { commonDark } from '../../_styles/common' +import common from './_common' + +export const pageHeaderDark: PageHeaderTheme = { + name: 'PageHeader', + common: commonDark, + self (vars) { + const { + textColor1, + textColor2, + textColor3, + fontSize, + fontWeightStrong, + primaryColorHover, + primaryColorPressed + } = vars + return { + ...common, + titleFontWeight: fontWeightStrong, + fontSize, + titleTextColor: textColor1, + backColor: textColor2, + backColorHover: primaryColorHover, + backColorPressed: primaryColorPressed, + subtitleTextColor: textColor3 + } + } +} diff --git a/src/page-header/styles/index.ts b/src/page-header/styles/index.ts new file mode 100644 index 000000000..8ba3655c1 --- /dev/null +++ b/src/page-header/styles/index.ts @@ -0,0 +1,3 @@ +export { pageHeaderLight } from './light' +export type { PageHeaderTheme, PageHeaderThemeVars } from './light' +export { pageHeaderDark } from './dark' diff --git a/src/page-header/styles/light.ts b/src/page-header/styles/light.ts new file mode 100644 index 000000000..980ce55d8 --- /dev/null +++ b/src/page-header/styles/light.ts @@ -0,0 +1,35 @@ +import { createTheme } from '../../_mixins' +import { commonLight } from '../../_styles/common' +import type { ThemeCommonVars } from '../../_styles/common' +import common from './_common' + +function self (vars: ThemeCommonVars) { + const { + textColor1, + textColor2, + textColor3, + fontSize, + fontWeightStrong, + primaryColorHover, + primaryColorPressed + } = vars + return { + ...common, + titleFontWeight: fontWeightStrong, + fontSize, + titleTextColor: textColor1, + backColor: textColor2, + backColorHover: primaryColorHover, + backColorPressed: primaryColorPressed, + subtitleTextColor: textColor3 + } +} + +export const pageHeaderLight = createTheme({ + name: 'PageHeader', + common: commonLight, + self +}) + +export type PageHeaderThemeVars = ReturnType +export type PageHeaderTheme = typeof pageHeaderLight