refactor:list (#6241)

* refactor:list

* fix inheritAttrs: false & attrs.class
This commit is contained in:
果冻橙 2023-02-08 14:14:08 +08:00 committed by GitHub
parent 26f98b7b10
commit de77b0175d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 377 additions and 505 deletions

View File

@ -1,6 +1,7 @@
import type { App, Plugin, ExtractPropTypes, PropType, HTMLAttributes } from 'vue'; import type { App, Plugin, ExtractPropTypes, PropType, HTMLAttributes } from 'vue';
import { provide, defineComponent, ref, watch, computed, toRef } from 'vue'; import { provide, defineComponent, ref, watch, computed, toRef } from 'vue';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import classNames from '../_util/classNames';
import type { SpinProps } from '../spin'; import type { SpinProps } from '../spin';
import Spin from '../spin'; import Spin from '../spin';
@ -19,6 +20,9 @@ import type { Breakpoint } from '../_util/responsiveObserve';
import { responsiveArray } from '../_util/responsiveObserve'; import { responsiveArray } from '../_util/responsiveObserve';
import eagerComputed from '../_util/eagerComputed'; import eagerComputed from '../_util/eagerComputed';
// CSSINJS
import useStyle from './style';
export type { ListItemProps } from './Item'; export type { ListItemProps } from './Item';
export type { ListItemMetaProps } from './ItemMeta'; export type { ListItemMetaProps } from './ItemMeta';
@ -79,6 +83,7 @@ import type { RenderEmptyHandler } from '../config-provider/renderEmpty';
const List = defineComponent({ const List = defineComponent({
compatConfig: { MODE: 3 }, compatConfig: { MODE: 3 },
name: 'AList', name: 'AList',
inheritAttrs: false,
Item, Item,
props: initDefaultProps(listProps(), { props: initDefaultProps(listProps(), {
dataSource: [], dataSource: [],
@ -88,7 +93,7 @@ const List = defineComponent({
pagination: false, pagination: false,
}), }),
slots: ['extra', 'loadMore', 'renderItem', 'header', 'footer'], slots: ['extra', 'loadMore', 'renderItem', 'header', 'footer'],
setup(props, { slots }) { setup(props, { slots, attrs }) {
provide(ListContextKey, { provide(ListContextKey, {
grid: toRef(props, 'grid'), grid: toRef(props, 'grid'),
itemLayout: toRef(props, 'itemLayout'), itemLayout: toRef(props, 'itemLayout'),
@ -98,6 +103,10 @@ const List = defineComponent({
total: 0, total: 0,
}; };
const { prefixCls, direction, renderEmpty } = useConfigInject('list', props); const { prefixCls, direction, renderEmpty } = useConfigInject('list', props);
// Style
const [wrapSSR, hashId] = useStyle(prefixCls);
const paginationObj = computed(() => const paginationObj = computed(() =>
props.pagination && typeof props.pagination === 'object' ? props.pagination : {}, props.pagination && typeof props.pagination === 'object' ? props.pagination : {},
); );
@ -260,10 +269,14 @@ const List = defineComponent({
const header = props.header ?? slots.header?.(); const header = props.header ?? slots.header?.();
const children = flattenChildren(slots.default?.()); const children = flattenChildren(slots.default?.());
const isSomethingAfterLastItem = !!(loadMore || props.pagination || footer); const isSomethingAfterLastItem = !!(loadMore || props.pagination || footer);
const classString = { const classString = classNames(
...classObj.value, {
[`${prefixCls.value}-something-after-last-item`]: isSomethingAfterLastItem, ...classObj.value,
}; [`${prefixCls.value}-something-after-last-item`]: isSomethingAfterLastItem,
},
attrs.class,
hashId.value,
);
const paginationContent = props.pagination ? ( const paginationContent = props.pagination ? (
<div class={`${prefixCls.value}-pagination`}> <div class={`${prefixCls.value}-pagination`}>
<Pagination <Pagination
@ -295,8 +308,8 @@ const List = defineComponent({
} }
const paginationPosition = paginationProps.value.position || 'bottom'; const paginationPosition = paginationProps.value.position || 'bottom';
return ( return wrapSSR(
<div class={classString}> <div {...attrs} class={classString}>
{(paginationPosition === 'top' || paginationPosition === 'both') && paginationContent} {(paginationPosition === 'top' || paginationPosition === 'both') && paginationContent}
{header && <div class={`${prefixCls.value}-header`}>{header}</div>} {header && <div class={`${prefixCls.value}-header`}>{header}</div>}
<Spin {...loadingProp.value}> <Spin {...loadingProp.value}>
@ -307,7 +320,7 @@ const List = defineComponent({
{loadMore || {loadMore ||
((paginationPosition === 'bottom' || paginationPosition === 'both') && ((paginationPosition === 'bottom' || paginationPosition === 'both') &&
paginationContent)} paginationContent)}
</div> </div>,
); );
}; };
}, },

View File

@ -1,44 +0,0 @@
@import '../../style/themes/index';
.@{list-prefix-cls}-bordered {
border: 1px solid @border-color-base;
border-radius: @border-radius-base;
.@{list-prefix-cls}-header {
padding-right: @padding-lg;
padding-left: @padding-lg;
}
.@{list-prefix-cls}-footer {
padding-right: @padding-lg;
padding-left: @padding-lg;
}
.@{list-prefix-cls}-item {
padding-right: @padding-lg;
padding-left: @padding-lg;
}
.@{list-prefix-cls}-pagination {
margin: @margin-md @margin-lg;
}
&.@{list-prefix-cls}-sm {
.@{list-prefix-cls}-item {
padding: @list-item-padding-sm;
}
.@{list-prefix-cls}-header,
.@{list-prefix-cls}-footer {
padding: @list-item-padding-sm;
}
}
&.@{list-prefix-cls}-lg {
.@{list-prefix-cls}-item {
padding: @list-item-padding-lg;
}
.@{list-prefix-cls}-header,
.@{list-prefix-cls}-footer {
padding: @list-item-padding-lg;
}
}
}

View File

@ -1,13 +0,0 @@
@import '../../style/themes/index';
@list-prefix-cls: ~'@{ant-prefix}-list';
@card-prefix-cls: ~'@{ant-prefix}-card';
.@{list-prefix-cls} {
// =================== Dard Hook Components ===================
.@{card-prefix-cls} {
& when (@theme = dark) {
background: @list-customize-card-bg;
}
}
}

View File

@ -1,249 +0,0 @@
@import '../../style/themes/index';
@import '../../style/mixins/index';
@import './customize.less';
@list-prefix-cls: ~'@{ant-prefix}-list';
.@{list-prefix-cls} {
.reset-component();
position: relative;
* {
outline: none;
}
&-pagination {
margin-top: @margin-lg;
text-align: right;
// https://github.com/ant-design/ant-design/issues/20037
.@{ant-prefix}-pagination-options {
text-align: left;
}
}
&-more {
margin-top: @margin-sm;
text-align: center;
button {
padding-right: 32px;
padding-left: 32px;
}
}
&-spin {
min-height: 40px;
text-align: center;
}
&-empty-text {
padding: @list-empty-text-padding;
color: @disabled-color;
font-size: @font-size-base;
text-align: center;
}
&-items {
margin: 0;
padding: 0;
list-style: none;
}
&-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: @list-item-padding;
color: @text-color;
&-meta {
display: flex;
flex: 1;
align-items: flex-start;
max-width: 100%;
&-avatar {
margin-right: @list-item-meta-avatar-margin-right;
}
&-content {
flex: 1 0;
width: 0;
color: @text-color;
}
&-title {
margin-bottom: 4px;
color: @text-color;
font-size: @font-size-base;
line-height: @line-height-base;
> a {
color: @text-color;
transition: all 0.3s;
&:hover {
color: @primary-color;
}
}
}
&-description {
color: @text-color-secondary;
font-size: @list-item-meta-description-font-size;
line-height: @line-height-base;
}
}
&-action {
flex: 0 0 auto;
margin-left: 48px;
padding: 0;
font-size: 0;
list-style: none;
& > li {
position: relative;
display: inline-block;
padding: 0 @padding-xs;
color: @text-color-secondary;
font-size: @font-size-base;
line-height: @line-height-base;
text-align: center;
&:first-child {
padding-left: 0;
}
}
&-split {
position: absolute;
top: 50%;
right: 0;
width: 1px;
height: 14px;
margin-top: -7px;
background-color: @border-color-split;
}
}
}
&-header {
background: @list-header-background;
}
&-footer {
background: @list-footer-background;
}
&-header,
&-footer {
padding-top: @padding-sm;
padding-bottom: @padding-sm;
}
&-empty {
padding: @padding-md 0;
color: @text-color-secondary;
font-size: 12px;
text-align: center;
}
&-split &-item {
border-bottom: 1px solid @border-color-split;
&:last-child {
border-bottom: none;
}
}
&-split &-header {
border-bottom: 1px solid @border-color-split;
}
&-split&-empty &-footer {
border-top: 1px solid @border-color-split;
}
&-loading &-spin-nested-loading {
min-height: 32px;
}
&-split&-something-after-last-item .@{ant-prefix}-spin-container > &-items > &-item:last-child {
border-bottom: 1px solid @border-color-split;
}
&-lg &-item {
padding: @list-item-padding-lg;
}
&-sm &-item {
padding: @list-item-padding-sm;
}
&-vertical &-item {
align-items: initial;
&-main {
display: block;
flex: 1;
}
&-extra {
margin-left: 40px;
}
&-meta {
margin-bottom: @list-item-meta-margin-bottom;
&-title {
margin-bottom: @list-item-meta-title-margin-bottom;
color: @heading-color;
font-size: @font-size-lg;
line-height: 24px;
}
}
&-action {
margin-top: @padding-md;
margin-left: auto;
> li {
padding: 0 @padding-md;
&:first-child {
padding-left: 0;
}
}
}
}
&-grid .@{ant-prefix}-col > &-item {
display: block;
max-width: 100%;
margin-bottom: @margin-md;
padding-top: 0;
padding-bottom: 0;
border-bottom: none;
}
// ============================ without flex ============================
&-item-no-flex {
display: block;
}
// Horizontal
&:not(.@{list-prefix-cls}-vertical) {
.@{list-prefix-cls}-item-no-flex {
.@{list-prefix-cls}-item-action {
float: right;
}
}
}
}
@import './bordered';
@import './responsive';
@import './rtl';

View File

@ -1,7 +1,354 @@
import '../../style/index.less'; import type { CSSObject } from '../../_util/cssinjs';
import './index.less'; import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
import { resetComponent } from '../../_style';
// style dependencies export interface ComponentToken {
import '../../empty/style'; contentWidth: number;
import '../../spin/style'; }
import '../../pagination/style';
interface ListToken extends FullToken<'List'> {
listBorderedCls: string;
minHeight: number;
listItemPaddingLG: string;
listItemPaddingSM: string;
listItemPadding: string;
}
const genBorderedStyle = (token: ListToken): CSSObject => {
const {
listBorderedCls,
componentCls,
paddingLG,
margin,
padding,
listItemPaddingSM,
marginLG,
borderRadiusLG,
} = token;
return {
[`${listBorderedCls}`]: {
border: `${token.lineWidth}px ${token.lineType} ${token.colorBorder}`,
borderRadius: borderRadiusLG,
[`${componentCls}-header,${componentCls}-footer,${componentCls}-item`]: {
paddingInline: paddingLG,
},
[`${componentCls}-pagination`]: {
margin: `${margin}px ${marginLG}px`,
},
},
[`${listBorderedCls}${componentCls}-sm`]: {
[`${componentCls}-item,${componentCls}-header,${componentCls}-footer`]: {
padding: listItemPaddingSM,
},
},
[`${listBorderedCls}${componentCls}-lg`]: {
[`${componentCls}-item,${componentCls}-header,${componentCls}-footer`]: {
padding: `${padding}px ${paddingLG}px`,
},
},
};
};
const genResponsiveStyle = (token: ListToken): CSSObject => {
const { componentCls, screenSM, screenMD, marginLG, marginSM, margin } = token;
return {
[`@media screen and (max-width:${screenMD})`]: {
[`${componentCls}`]: {
[`${componentCls}-item`]: {
[`${componentCls}-item-action`]: {
marginInlineStart: marginLG,
},
},
},
[`${componentCls}-vertical`]: {
[`${componentCls}-item`]: {
[`${componentCls}-item-extra`]: {
marginInlineStart: marginLG,
},
},
},
},
[`@media screen and (max-width: ${screenSM})`]: {
[`${componentCls}`]: {
[`${componentCls}-item`]: {
flexWrap: 'wrap',
[`${componentCls}-action`]: {
marginInlineStart: marginSM,
},
},
},
[`${componentCls}-vertical`]: {
[`${componentCls}-item`]: {
flexWrap: 'wrap-reverse',
[`${componentCls}-item-main`]: {
minWidth: token.contentWidth,
},
[`${componentCls}-item-extra`]: {
margin: `auto auto ${margin}px`,
},
},
},
},
};
};
// =============================== Base ===============================
const genBaseStyle: GenerateStyle<ListToken> = token => {
const {
componentCls,
antCls,
controlHeight,
minHeight,
paddingSM,
marginLG,
padding,
listItemPadding,
colorPrimary,
listItemPaddingSM,
listItemPaddingLG,
paddingXS,
margin,
colorText,
colorTextDescription,
motionDurationSlow,
lineWidth,
} = token;
return {
[`${componentCls}`]: {
...resetComponent(token),
position: 'relative',
'*': {
outline: 'none',
},
[`${componentCls}-header, ${componentCls}-footer`]: {
background: 'transparent',
paddingBlock: paddingSM,
},
[`${componentCls}-pagination`]: {
marginBlockStart: marginLG,
textAlign: 'end',
// https://github.com/ant-design/ant-design/issues/20037
[`${antCls}-pagination-options`]: {
textAlign: 'start',
},
},
[`${componentCls}-spin`]: {
minHeight,
textAlign: 'center',
},
[`${componentCls}-items`]: {
margin: 0,
padding: 0,
listStyle: 'none',
},
[`${componentCls}-item`]: {
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
padding: listItemPadding,
color: colorText,
[`${componentCls}-item-meta`]: {
display: 'flex',
flex: 1,
alignItems: 'flex-start',
maxWidth: '100%',
[`${componentCls}-item-meta-avatar`]: {
marginInlineEnd: padding,
},
[`${componentCls}-item-meta-content`]: {
flex: '1 0',
width: 0,
color: colorText,
},
[`${componentCls}-item-meta-title`]: {
marginBottom: token.marginXXS,
color: colorText,
fontSize: token.fontSize,
lineHeight: token.lineHeight,
'> a': {
color: colorText,
transition: `all ${motionDurationSlow}`,
[`&:hover`]: {
color: colorPrimary,
},
},
},
[`${componentCls}-item-meta-description`]: {
color: colorTextDescription,
fontSize: token.fontSize,
lineHeight: token.lineHeight,
},
},
[`${componentCls}-item-action`]: {
flex: '0 0 auto',
marginInlineStart: token.marginXXL,
padding: 0,
fontSize: 0,
listStyle: 'none',
[`& > li`]: {
position: 'relative',
display: 'inline-block',
padding: `0 ${paddingXS}px`,
color: colorTextDescription,
fontSize: token.fontSize,
lineHeight: token.lineHeight,
textAlign: 'center',
[`&:first-child`]: {
paddingInlineStart: 0,
},
},
[`${componentCls}-item-action-split`]: {
position: 'absolute',
insetBlockStart: '50%',
insetInlineEnd: 0,
width: lineWidth,
height: Math.ceil(token.fontSize * token.lineHeight) - token.marginXXS * 2,
transform: 'translateY(-50%)',
backgroundColor: token.colorSplit,
},
},
},
[`${componentCls}-empty`]: {
padding: `${padding}px 0`,
color: colorTextDescription,
fontSize: token.fontSizeSM,
textAlign: 'center',
},
[`${componentCls}-empty-text`]: {
padding,
color: token.colorTextDisabled,
fontSize: token.fontSize,
textAlign: 'center',
},
// ============================ without flex ============================
[`${componentCls}-item-no-flex`]: {
display: 'block',
},
},
[`${componentCls}-grid ${antCls}-col > ${componentCls}-item`]: {
display: 'block',
maxWidth: '100%',
marginBlockEnd: margin,
paddingBlock: 0,
borderBlockEnd: 'none',
},
[`${componentCls}-vertical ${componentCls}-item`]: {
alignItems: 'initial',
[`${componentCls}-item-main`]: {
display: 'block',
flex: 1,
},
[`${componentCls}-item-extra`]: {
marginInlineStart: marginLG,
},
[`${componentCls}-item-meta`]: {
marginBlockEnd: padding,
[`${componentCls}-item-meta-title`]: {
marginBlockEnd: paddingSM,
color: colorText,
fontSize: token.fontSizeLG,
lineHeight: token.lineHeightLG,
},
},
[`${componentCls}-item-action`]: {
marginBlockStart: padding,
marginInlineStart: 'auto',
'> li': {
padding: `0 ${padding}px`,
[`&:first-child`]: {
paddingInlineStart: 0,
},
},
},
},
[`${componentCls}-split ${componentCls}-item`]: {
borderBlockEnd: `${token.lineWidth}px ${token.lineType} ${token.colorSplit}`,
[`&:last-child`]: {
borderBlockEnd: 'none',
},
},
[`${componentCls}-split ${componentCls}-header`]: {
borderBlockEnd: `${token.lineWidth}px ${token.lineType} ${token.colorSplit}`,
},
[`${componentCls}-split${componentCls}-empty ${componentCls}-footer`]: {
borderTop: `${token.lineWidth}px ${token.lineType} ${token.colorSplit}`,
},
[`${componentCls}-loading ${componentCls}-spin-nested-loading`]: {
minHeight: controlHeight,
},
[`${componentCls}-split${componentCls}-something-after-last-item ${antCls}-spin-container > ${componentCls}-items > ${componentCls}-item:last-child`]:
{
borderBlockEnd: `${token.lineWidth}px ${token.lineType} ${token.colorSplit}`,
},
[`${componentCls}-lg ${componentCls}-item`]: {
padding: listItemPaddingLG,
},
[`${componentCls}-sm ${componentCls}-item`]: {
padding: listItemPaddingSM,
},
// Horizontal
[`${componentCls}:not(${componentCls}-vertical)`]: {
[`${componentCls}-item-no-flex`]: {
[`${componentCls}-item-action`]: {
float: 'right',
},
},
},
};
};
// ============================== Export ==============================
export default genComponentStyleHook(
'List',
token => {
const listToken = mergeToken<ListToken>(token, {
listBorderedCls: `${token.componentCls}-bordered`,
minHeight: token.controlHeightLG,
listItemPadding: `${token.paddingContentVertical}px ${token.paddingContentHorizontalLG}px`,
listItemPaddingSM: `${token.paddingContentVerticalSM}px ${token.paddingContentHorizontal}px`,
listItemPaddingLG: `${token.paddingContentVerticalLG}px ${token.paddingContentHorizontalLG}px`,
});
return [genBaseStyle(listToken), genBorderedStyle(listToken), genResponsiveStyle(listToken)];
},
{
contentWidth: 220,
},
);

View File

@ -1,43 +0,0 @@
@media screen and (max-width: @screen-md) {
.@{list-prefix-cls} {
&-item {
&-action {
margin-left: 24px;
}
}
}
.@{list-prefix-cls}-vertical {
.@{list-prefix-cls}-item {
&-extra {
margin-left: 24px;
}
}
}
}
@media screen and (max-width: @screen-sm) {
.@{list-prefix-cls} {
&-item {
flex-wrap: wrap;
&-action {
margin-left: 12px;
}
}
}
.@{list-prefix-cls}-vertical {
.@{list-prefix-cls}-item {
flex-wrap: wrap-reverse;
&-main {
min-width: 220px;
}
&-extra {
margin: auto auto 16px;
}
}
}
}

View File

@ -1,139 +0,0 @@
@import '../../style/themes/index';
@import '../../style/mixins/index';
@import './customize.less';
@list-prefix-cls: ~'@{ant-prefix}-list';
.@{list-prefix-cls} {
&-rtl {
direction: rtl;
text-align: right;
// fix for virtual scroll style attribute > (direction:ltr)
.ReactVirtualized__List .@{list-prefix-cls}-item {
direction: rtl;
}
}
&-pagination {
.@{list-prefix-cls}-rtl & {
text-align: left;
}
}
&-item {
&-meta {
&-avatar {
.@{list-prefix-cls}-rtl & {
margin-right: 0;
margin-left: @list-item-meta-avatar-margin-right;
}
}
}
&-action {
.@{list-prefix-cls}-rtl & {
margin-right: 48px;
margin-left: 0;
}
& > li:first-child {
.@{list-prefix-cls}.@{list-prefix-cls}-rtl & {
padding-right: 0;
padding-left: @padding-md;
}
}
&-split {
.@{list-prefix-cls}-rtl & {
right: auto;
left: 0;
}
}
}
}
&-vertical &-item {
&-extra {
.@{list-prefix-cls}-rtl& {
margin-right: 40px;
margin-left: 0;
}
}
&-action {
.@{list-prefix-cls}-rtl& {
margin-right: auto;
}
> li {
&:first-child {
.@{list-prefix-cls}-rtl & {
padding-right: 0;
padding-left: @padding-md;
}
}
}
}
}
// Horizontal
&:not(.@{list-prefix-cls}-vertical) {
.@{list-prefix-cls}-item-no-flex {
.@{list-prefix-cls}-item-action {
.@{list-prefix-cls}-rtl & {
float: left;
}
}
}
}
}
// responsive
@media screen and (max-width: @screen-md) {
.@{list-prefix-cls} {
&-item {
&-action {
.@{list-prefix-cls}-rtl & {
margin-right: 24px;
margin-left: 0;
}
}
}
}
.@{list-prefix-cls}-vertical {
.@{list-prefix-cls}-item {
&-extra {
.@{list-prefix-cls}-rtl & {
margin-right: 24px;
margin-left: 0;
}
}
}
}
}
@media screen and (max-width: @screen-sm) {
.@{list-prefix-cls} {
&-item {
&-action {
.@{list-prefix-cls}-rtl & {
margin-right: 22px;
margin-left: 0;
}
}
}
}
.@{list-prefix-cls}-vertical {
.@{list-prefix-cls}-item {
&-extra {
// to override margins on rtl view
.@{list-prefix-cls}-rtl& {
margin: auto auto 16px;
}
}
}
}
}

View File

@ -46,7 +46,7 @@ import './tree/style';
import './upload/style'; import './upload/style';
import './layout/style'; import './layout/style';
// import './anchor/style'; // import './anchor/style';
import './list/style'; // import './list/style';
import './tree-select/style'; import './tree-select/style';
import './drawer/style'; import './drawer/style';
// import './skeleton/style'; // import './skeleton/style';

View File

@ -18,7 +18,7 @@ import type { ComponentToken as EmptyComponentToken } from '../../empty/style';
// import type { ComponentToken as ImageComponentToken } from '../../image/style'; // import type { ComponentToken as ImageComponentToken } from '../../image/style';
// import type { ComponentToken as InputNumberComponentToken } from '../../input-number/style'; // import type { ComponentToken as InputNumberComponentToken } from '../../input-number/style';
// import type { ComponentToken as LayoutComponentToken } from '../../layout/style'; // import type { ComponentToken as LayoutComponentToken } from '../../layout/style';
// import type { ComponentToken as ListComponentToken } from '../../list/style'; import type { ComponentToken as ListComponentToken } from '../../list/style';
// import type { ComponentToken as MentionsComponentToken } from '../../mentions/style'; // import type { ComponentToken as MentionsComponentToken } from '../../mentions/style';
import type { ComponentToken as MenuComponentToken } from '../../menu/style'; import type { ComponentToken as MenuComponentToken } from '../../menu/style';
import type { ComponentToken as MessageComponentToken } from '../../message/style'; import type { ComponentToken as MessageComponentToken } from '../../message/style';
@ -78,7 +78,7 @@ export interface ComponentTokenMap {
Input?: {}; Input?: {};
// InputNumber?: InputNumberComponentToken; // InputNumber?: InputNumberComponentToken;
// Layout?: LayoutComponentToken; // Layout?: LayoutComponentToken;
// List?: ListComponentToken; List?: ListComponentToken;
// Mentions?: MentionsComponentToken; // Mentions?: MentionsComponentToken;
Notification?: NotificationComponentToken; Notification?: NotificationComponentToken;
PageHeader?: {}; PageHeader?: {};