diff --git a/components/list/index.tsx b/components/list/index.tsx index b6ca7ba71..2fbd6b704 100644 --- a/components/list/index.tsx +++ b/components/list/index.tsx @@ -1,6 +1,7 @@ import type { App, Plugin, ExtractPropTypes, PropType, HTMLAttributes } from 'vue'; import { provide, defineComponent, ref, watch, computed, toRef } from 'vue'; import PropTypes from '../_util/vue-types'; +import classNames from '../_util/classNames'; import type { SpinProps } from '../spin'; import Spin from '../spin'; @@ -19,6 +20,9 @@ import type { Breakpoint } from '../_util/responsiveObserve'; import { responsiveArray } from '../_util/responsiveObserve'; import eagerComputed from '../_util/eagerComputed'; +// CSSINJS +import useStyle from './style'; + export type { ListItemProps } from './Item'; export type { ListItemMetaProps } from './ItemMeta'; @@ -79,6 +83,7 @@ import type { RenderEmptyHandler } from '../config-provider/renderEmpty'; const List = defineComponent({ compatConfig: { MODE: 3 }, name: 'AList', + inheritAttrs: false, Item, props: initDefaultProps(listProps(), { dataSource: [], @@ -88,7 +93,7 @@ const List = defineComponent({ pagination: false, }), slots: ['extra', 'loadMore', 'renderItem', 'header', 'footer'], - setup(props, { slots }) { + setup(props, { slots, attrs }) { provide(ListContextKey, { grid: toRef(props, 'grid'), itemLayout: toRef(props, 'itemLayout'), @@ -98,6 +103,10 @@ const List = defineComponent({ total: 0, }; const { prefixCls, direction, renderEmpty } = useConfigInject('list', props); + + // Style + const [wrapSSR, hashId] = useStyle(prefixCls); + const paginationObj = computed(() => props.pagination && typeof props.pagination === 'object' ? props.pagination : {}, ); @@ -260,10 +269,14 @@ const List = defineComponent({ const header = props.header ?? slots.header?.(); const children = flattenChildren(slots.default?.()); const isSomethingAfterLastItem = !!(loadMore || props.pagination || footer); - const classString = { - ...classObj.value, - [`${prefixCls.value}-something-after-last-item`]: isSomethingAfterLastItem, - }; + const classString = classNames( + { + ...classObj.value, + [`${prefixCls.value}-something-after-last-item`]: isSomethingAfterLastItem, + }, + attrs.class, + hashId.value, + ); const paginationContent = props.pagination ? (
+ return wrapSSR( +
{(paginationPosition === 'top' || paginationPosition === 'both') && paginationContent} {header &&
{header}
} @@ -307,7 +320,7 @@ const List = defineComponent({ {loadMore || ((paginationPosition === 'bottom' || paginationPosition === 'both') && paginationContent)} -
+
, ); }; }, diff --git a/components/list/style/bordered.less b/components/list/style/bordered.less deleted file mode 100644 index 63859a3eb..000000000 --- a/components/list/style/bordered.less +++ /dev/null @@ -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; - } - } -} diff --git a/components/list/style/customize.less b/components/list/style/customize.less deleted file mode 100644 index d0e2b84a8..000000000 --- a/components/list/style/customize.less +++ /dev/null @@ -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; - } - } -} diff --git a/components/list/style/index.less b/components/list/style/index.less deleted file mode 100644 index 6804e2412..000000000 --- a/components/list/style/index.less +++ /dev/null @@ -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'; diff --git a/components/list/style/index.tsx b/components/list/style/index.tsx index 86a219e85..d763f7999 100644 --- a/components/list/style/index.tsx +++ b/components/list/style/index.tsx @@ -1,7 +1,354 @@ -import '../../style/index.less'; -import './index.less'; +import type { CSSObject } from '../../_util/cssinjs'; +import type { FullToken, GenerateStyle } from '../../theme/internal'; +import { genComponentStyleHook, mergeToken } from '../../theme/internal'; +import { resetComponent } from '../../_style'; -// style dependencies -import '../../empty/style'; -import '../../spin/style'; -import '../../pagination/style'; +export interface ComponentToken { + contentWidth: number; +} + +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 = 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(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, + }, +); diff --git a/components/list/style/responsive.less b/components/list/style/responsive.less deleted file mode 100644 index db7a108a3..000000000 --- a/components/list/style/responsive.less +++ /dev/null @@ -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; - } - } - } -} diff --git a/components/list/style/rtl.less b/components/list/style/rtl.less deleted file mode 100644 index 429969c68..000000000 --- a/components/list/style/rtl.less +++ /dev/null @@ -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; - } - } - } - } -} diff --git a/components/style.ts b/components/style.ts index fc400a7b5..23700052a 100644 --- a/components/style.ts +++ b/components/style.ts @@ -46,7 +46,7 @@ import './tree/style'; import './upload/style'; import './layout/style'; // import './anchor/style'; -import './list/style'; +// import './list/style'; import './tree-select/style'; import './drawer/style'; // import './skeleton/style'; diff --git a/components/theme/interface/components.ts b/components/theme/interface/components.ts index 6ec0d40f2..cfd7a4500 100644 --- a/components/theme/interface/components.ts +++ b/components/theme/interface/components.ts @@ -18,7 +18,7 @@ import type { ComponentToken as EmptyComponentToken } from '../../empty/style'; // import type { ComponentToken as ImageComponentToken } from '../../image/style'; // import type { ComponentToken as InputNumberComponentToken } from '../../input-number/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 MenuComponentToken } from '../../menu/style'; import type { ComponentToken as MessageComponentToken } from '../../message/style'; @@ -78,7 +78,7 @@ export interface ComponentTokenMap { Input?: {}; // InputNumber?: InputNumberComponentToken; // Layout?: LayoutComponentToken; - // List?: ListComponentToken; + List?: ListComponentToken; // Mentions?: MentionsComponentToken; Notification?: NotificationComponentToken; PageHeader?: {};