Skip to content

Commit 6446f0f

Browse files
committed
Migrate article list to entry card list
1 parent fe3eed5 commit 6446f0f

File tree

5 files changed

+245
-66
lines changed

5 files changed

+245
-66
lines changed

com.woltlab.wcf/templates/articleList.tpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939

4040
{include file='header'}
4141

42-
<div class="section">
42+
<div class="section entryCardList__container">
4343
{unsafe:$listView->render()}
4444
</div>
4545

com.woltlab.wcf/templates/articleListItems.tpl

Lines changed: 68 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
{foreach from=$view->getItems() item='article' name='articles'}
44
{if $article->getArticleContent()}
5-
<article class="contentItem contentItemMultiColumn listView__item" data-object-id="{$article->getObjectID()}">
6-
<div class="contentItemOptions">
5+
<article class="entryCardList__item listView__item" data-object-id="{$article->getObjectID()}">
6+
<div class="entryCardList__item__buttons">
77
{if $view->hasBulkInteractions()}
88
<label class="button small jsTooltip" title="{lang}wcf.clipboard.item.mark{/lang}">
99
<input type="checkbox" class="listView__selectItem" aria-label="{lang}wcf.clipboard.item.mark{/lang}">
@@ -12,77 +12,81 @@
1212

1313
{unsafe:$view->renderInteractionContextMenuButton($article)}
1414
</div>
15-
16-
<div class="contentItemLink">
17-
<div class="contentItemImage contentItemImageLarge">
18-
<img
19-
class="contentItemImageElement"
20-
src="{if $article->getTeaserImage()}{$article->getTeaserImage()->getThumbnailLink('medium')}{else}{$__wcf->getStyleHandler()->getStyle()->getCoverPhotoURL()}{/if}"
21-
height="{if $article->getTeaserImage()}{@$article->getTeaserImage()->getThumbnailHeight('medium')}{else}{@$__wcf->getStyleHandler()->getStyle()->getCoverPhotoHeight()}{/if}"
22-
width="{if $article->getTeaserImage()}{@$article->getTeaserImage()->getThumbnailWidth('medium')}{else}{@$__wcf->getStyleHandler()->getStyle()->getCoverPhotoWidth()}{/if}"
23-
loading="lazy"
24-
alt="">
25-
26-
{hascontent}
27-
<div class="contentItemBadges">
28-
{content}
29-
{if $article->isDeleted}<span class="badge label red contentItemBadge contentItemBadgeIsDeleted">{lang}wcf.message.status.deleted{/lang}</span>{/if}
30-
{if !$article->isPublished()}<span class="badge label green contentItemBadge contentItemBadgeIsDisabled">{lang}wcf.message.status.disabled{/lang}</span>{/if}
31-
{if $article->isNew()}<span class="badge label contentItemBadge contentItemBadgeNew">{lang}wcf.message.new{/lang}</span>{/if}
32-
33-
{event name='contentItemBadges'}
34-
{/content}
35-
</div>
36-
{/hascontent}
37-
</div>
38-
39-
<div class="contentItemContent">
40-
{if $article->hasLabels()}
41-
<div class="contentItemLabels">
42-
{foreach from=$article->getLabels() item=label}
43-
{@$label->render('contentItemLabel')}
44-
{/foreach}
45-
</div>
46-
{/if}
47-
48-
<h2 class="contentItemTitle"><a href="{$article->getLink()}" class="contentItemTitleLink">{$article->getTitle()}</a></h2>
49-
50-
<div class="contentItemDescription">
51-
{@$article->getFormattedTeaser()}
15+
16+
<div class="entryCardList__item__image">
17+
<img
18+
class="entryCardList__item__image__element"
19+
src="{if $article->getTeaserImage()}{$article->getTeaserImage()->getThumbnailLink('medium')}{else}{$__wcf->getStyleHandler()->getStyle()->getCoverPhotoURL()}{/if}"
20+
height="{if $article->getTeaserImage()}{$article->getTeaserImage()->getThumbnailHeight('medium')}{else}{$__wcf->getStyleHandler()->getStyle()->getCoverPhotoHeight()}{/if}"
21+
width="{if $article->getTeaserImage()}{$article->getTeaserImage()->getThumbnailWidth('medium')}{else}{$__wcf->getStyleHandler()->getStyle()->getCoverPhotoWidth()}{/if}"
22+
loading="lazy"
23+
alt=""
24+
>
25+
26+
{hascontent}
27+
<div class="entryCardList__item__badges">
28+
{content}
29+
{if $article->isDeleted}<span class="badge red">{lang}wcf.message.status.deleted{/lang}</span>{/if}
30+
{if !$article->isPublished()}<span class="badge green">{lang}wcf.message.status.disabled{/lang}</span>{/if}
31+
{if $article->isNew()}<span class="badge">{lang}wcf.message.new{/lang}</span>{/if}
32+
33+
{event name='contentItemBadges'}{* deprecated: use badges instead *}
34+
{event name='badges'}
35+
{/content}
5236
</div>
37+
{/hascontent}
38+
</div>
39+
40+
<div class="entryCardList__item__content">
41+
{if $article->hasLabels()}
42+
<ul class="entryCardList__item__labels labelList">
43+
{foreach from=$article->getLabels() item=label}
44+
<li>{unsafe:$label->render()}</li>
45+
{/foreach}
46+
</ul>
47+
{/if}
48+
49+
<h2 class="entryCardList__item__title">
50+
<a href="{$article->getLink()}" class="entryCardList__item__link">{$article->getTitle()}</a>
51+
</h2>
52+
53+
<div class="entryCardList__item__teaser">
54+
{unsafe:$article->getFormattedTeaser()}
5355
</div>
5456
</div>
55-
56-
<div class="contentItemMeta">
57-
<span class="contentItemMetaImage">
58-
{@$article->getUserProfile()->getAvatar()->getImageTag(32)}
59-
</span>
57+
58+
<div class="entryCardList__item__meta">
59+
<div class="entryCardList__item__meta__image">
60+
{unsafe:$article->getUserProfile()->getAvatar()->getImageTag(32)}
61+
</div>
6062

61-
<div class="contentItemMetaContent">
62-
<div class="contentItemMetaAuthor">
63-
{@$article->getUserProfile()->getFormattedUsername()}
63+
<div class="entryCardList__item__meta__content">
64+
<div class="entryCardList__item__meta__author">
65+
{unsafe:$article->getUserProfile()->getFormattedUsername()}
6466
</div>
65-
<div class="contentItemMetaTime">
66-
{@$article->time|time}
67+
68+
<div class="entryCardList__item__meta__time">
69+
{time time=$article->time}
6770
</div>
6871
</div>
69-
70-
<div class="contentItemMetaIcons">
72+
73+
<div class="entryCardList__item__meta__icons">
7174
{if MODULE_LIKE && $__wcf->getSession()->getPermission('user.like.canViewLike') && $article->cumulativeLikes}
72-
<div class="contentItemMetaIcon">
75+
<div class="entryCardList__item__meta__icon">
7376
{include file='shared_topReaction' cachedReactions=$article->cachedReactions render='short'}
7477
</div>
7578
{/if}
7679
{if $article->getDiscussionProvider()->getDiscussionCountPhrase()}{* empty phrase indicates that comments are disabled *}
77-
<div class="contentItemMetaIcon">
80+
<div class="entryCardList__item__meta__icon">
7881
{icon name='comments'}
7982
<span aria-label="{$article->getDiscussionProvider()->getDiscussionCountPhrase()}">
8083
{$article->getDiscussionProvider()->getDiscussionCount()}
8184
</span>
8285
</div>
8386
{/if}
8487

85-
{event name='contentItemMetaIcons'}
88+
{event name='contentItemMetaIcons'}{* deprecated: use badges instead *}
89+
{event name='metaIcons'}
8690
</div>
8791
</div>
8892
</article>
@@ -91,38 +95,38 @@
9195
{if MODULE_WCF_AD && !$disableAds}
9296
{if $tpl[foreach][articles][iteration] === 1}
9397
{hascontent}
94-
<div class="contentItem contentItemAd">
95-
{content}{@$__wcf->getAdHandler()->getAds('com.woltlab.wcf.article.after1stArticle')}{/content}
98+
<div class="entryCardList__item entryCardList__item--ad">
99+
{content}{unsafe:$__wcf->getAdHandler()->getAds('com.woltlab.wcf.article.after1stArticle')}{/content}
96100
</div>
97101
{/hascontent}
98102
{else}
99103
{if $tpl[foreach][articles][iteration] % 2 === 0}
100104
{hascontent}
101-
<div class="contentItem contentItemAd">
102-
{content}{@$__wcf->getAdHandler()->getAds('com.woltlab.wcf.article.afterEvery2ndArticle')}{/content}
105+
<div class="entryCardList__item entryCardList__item--ad">
106+
{content}{unsafe:$__wcf->getAdHandler()->getAds('com.woltlab.wcf.article.afterEvery2ndArticle')}{/content}
103107
</div>
104108
{/hascontent}
105109
{/if}
106110

107111
{if $tpl[foreach][articles][iteration] % 3 === 0}
108112
{hascontent}
109-
<div class="contentItem contentItemAd">
110-
{content}{@$__wcf->getAdHandler()->getAds('com.woltlab.wcf.article.afterEvery3rdArticle')}{/content}
113+
<div class="entryCardList__item entryCardList__item--ad">
114+
{content}{unsafe:$__wcf->getAdHandler()->getAds('com.woltlab.wcf.article.afterEvery3rdArticle')}{/content}
111115
</div>
112116
{/hascontent}
113117
{/if}
114118

115119
{if $tpl[foreach][articles][iteration] % 5 === 0}
116120
{hascontent}
117-
<div class="contentItem contentItemAd">
118-
{content}{@$__wcf->getAdHandler()->getAds('com.woltlab.wcf.article.afterEvery5thArticle')}{/content}
121+
<div class="entryCardList__item entryCardList__item--ad">
122+
{content}{unsafe:$__wcf->getAdHandler()->getAds('com.woltlab.wcf.article.afterEvery5thArticle')}{/content}
119123
</div>
120124
{/hascontent}
121125

122126
{if $tpl[foreach][articles][iteration] % 10 === 0}
123127
{hascontent}
124-
<div class="contentItem contentItemAd">
125-
{content}{@$__wcf->getAdHandler()->getAds('com.woltlab.wcf.article.afterEvery10thArticle')}{/content}
128+
<div class="entryCardList__item entryCardList__item--ad">
129+
{content}{unsafe:$__wcf->getAdHandler()->getAds('com.woltlab.wcf.article.afterEvery10thArticle')}{/content}
126130
</div>
127131
{/hascontent}
128132
{/if}

wcfsetup/install/files/lib/system/listView/user/ArticleListView.class.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public function __construct()
5555
$this->setItemsPerPage(\ARTICLES_PER_PAGE);
5656
$this->setSortField('time');
5757
$this->setSortOrder(\ARTICLE_SORT_ORDER);
58-
$this->setCssClassName('contentItemList');
58+
$this->setCssClassName('entryCardList');
5959
}
6060

6161
#[\Override]

wcfsetup/install/files/lib/system/tagging/TaggableArticle.class.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,10 @@ public function getListView(array $tagIDs): ArticleListView
2222
{
2323
return new TaggedArticleListView($tagIDs);
2424
}
25+
26+
#[\Override]
27+
public function getContainerCssClassName(): string
28+
{
29+
return 'entryCardList__container';
30+
}
2531
}
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
.entryCardList__container {
2+
container-type: inline-size;
3+
}
4+
5+
.entryCardList {
6+
display: grid;
7+
gap: 20px;
8+
grid-auto-rows: minmax(200px, auto);
9+
}
10+
11+
@container (min-width: 1000px) {
12+
.entryCardList {
13+
grid-template-columns: repeat(3, 1fr);
14+
}
15+
}
16+
17+
@container (width < 1000px) and (min-width: 620px) {
18+
.entryCardList {
19+
grid-template-columns: repeat(2, 1fr);
20+
}
21+
}
22+
23+
.entryCardList__item {
24+
background-color: var(--wcfContentBackground);
25+
border-radius: var(--wcfBorderRadius);
26+
box-shadow: var(--wcfBoxShadowCard);
27+
display: flex;
28+
flex-direction: column;
29+
position: relative;
30+
row-gap: 10px;
31+
}
32+
33+
html[data-color-scheme="dark"] {
34+
.entryCardList__item {
35+
border: 1px solid var(--wcfContentBorderInner);
36+
}
37+
}
38+
39+
.entryCardList__item__buttons {
40+
position: absolute;
41+
right: 10px;
42+
top: 10px;
43+
z-index: 1;
44+
align-items: flex-start;
45+
display: flex;
46+
gap: 5px;
47+
}
48+
49+
.entryCardList__item__image {
50+
border-top-left-radius: var(--wcfBorderRadius);
51+
border-top-right-radius: var(--wcfBorderRadius);
52+
overflow: hidden;
53+
position: relative;
54+
}
55+
56+
.entryCardList__item__image__element {
57+
height: 150px;
58+
object-fit: cover;
59+
object-position: center;
60+
width: 100%;
61+
}
62+
63+
html:not(.touch) .entryCardList__item__image__element {
64+
transition: transform 0.24s linear;
65+
}
66+
67+
html:not(.touch) .entryCardList__item:hover .entryCardList__item__image__element {
68+
transform: scale(1.05);
69+
}
70+
71+
.entryCardList__item__badges {
72+
align-items: flex-start;
73+
display: flex;
74+
flex-direction: column;
75+
left: 10px;
76+
position: absolute;
77+
top: 10px;
78+
z-index: 1;
79+
}
80+
81+
.entryCardList__item__content {
82+
display: flex;
83+
flex: 1 auto;
84+
flex-direction: column;
85+
padding: 10px;
86+
row-gap: 10px;
87+
}
88+
89+
.entryCardList__item__title {
90+
color: var(--wcfContentHeadlineLink);
91+
92+
@include wcfFontHeadline;
93+
@include wcfFontBold;
94+
}
95+
96+
.entryCardList__item__link {
97+
color: inherit;
98+
99+
&::before {
100+
content: "";
101+
inset: 0;
102+
position: absolute;
103+
}
104+
105+
&:hover,
106+
&:focus {
107+
color: inherit;
108+
}
109+
}
110+
111+
.entryCardList__item__teaser {
112+
color: var(--wcfContentDimmedText);
113+
}
114+
115+
.entryCardList__item__meta {
116+
align-items: center;
117+
border-top: 1px solid var(--wcfContentBorderInner);
118+
color: var(--wcfContentDimmedText);
119+
display: flex;
120+
flex: 0 auto;
121+
padding: 10px;
122+
white-space: nowrap;
123+
gap: 10px;
124+
125+
.icon {
126+
color: inherit;
127+
}
128+
}
129+
130+
.entryCardList__item__meta__image {
131+
flex: 0 auto;
132+
}
133+
134+
.entryCardList__item__meta__content {
135+
flex: 1 auto;
136+
137+
@include wcfFontSmall;
138+
}
139+
140+
.entryCardList__item__meta__author {
141+
color: var(--wcfContentText);
142+
143+
a,
144+
a:hover {
145+
color: inherit;
146+
}
147+
}
148+
149+
.entryCardList__item__meta__icons {
150+
align-items: center;
151+
display: flex;
152+
flex: 0 auto;
153+
gap: 10px;
154+
155+
@include wcfFontSmall;
156+
}
157+
158+
.entryCardList__item__meta__icon {
159+
flex: 0 auto;
160+
161+
.topReactionShort {
162+
align-items: center;
163+
display: flex;
164+
}
165+
166+
.reactionType {
167+
margin-right: 3px;
168+
}
169+
}

0 commit comments

Comments
 (0)