Added static result table for members filtering

- Added horizontal scrolling, fullscreen table component.
- Mocked multiple columns in the members result list.

Alpha feature.
This commit is contained in:
Peter Zimon 2021-07-22 15:47:01 +02:00
parent 3e82a19791
commit d356f5451a
6 changed files with 292 additions and 81 deletions

View File

@ -1,61 +1,188 @@
<li class="gh-list-row gh-members-list-item {{if @member.is_loading "loading"}}" data-test-member={{@member.id}} ...attributes>
{{#if @member.is_loading}}
<div class="gh-list-data gh-members-list-basic gh-list-loadingcell">
<div class="gh-list-loading-title"></div>
<div class="gh-list-loading-detail"></div>
</div>
<div class="gh-list-data gh-members-list-open-rate gh-list-cellwidth-10"></div>
<div class="gh-list-data gh-members-list-geolocation gh-list-cellwidth-10"></div>
<div class="gh-list-data gh-members-list-subscribed-at gh-list-cellwidth-10"></div>
<div class="gh-list-data gh-members-list-chevron gh-list-cellwidth-chevron"></div>
{{else}}
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data gh-members-list-basic">
<div class="flex items-center">
<GhMemberAvatar @member={{@member}} @containerClass="w9 h9 mr3 flex-shrink-0" />
<div class="w-80">
<h3 class="ma0 pa0 gh-members-list-name {{if (not @member.name) "gh-members-name-noname"}}">{{or @member.name @member.email}}</h3>
{{#if @member.name}}
<p class="ma0 pa0 middarkgrey f8 gh-members-list-email">{{@member.email}}</p>
{{/if}}
</div>
{{#if (feature "membersFiltering")}}
<tr>
{{#if @member.is_loading}}
<div class="gh-list-data gh-members-list-basic gh-list-loadingcell">
<div class="gh-list-loading-title"></div>
<div class="gh-list-loading-detail"></div>
</div>
</LinkTo>
<div class="gh-list-data"></div>
<div class="gh-list-data"></div>
<div class="gh-list-data"></div>
<div class="gh-list-data"></div>
<div class="gh-list-data"></div>
<div class="gh-list-data"></div>
<div class="gh-list-data"></div>
<div class="gh-list-data"></div>
<div class="gh-list-data"></div>
{{else}}
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data">
<div class="flex items-center">
<GhMemberAvatar @member={{@member}} @containerClass="w9 h9 mr3 flex-shrink-0" />
<div class="w-80">
<h3 class="ma0 pa0 gh-members-list-name {{if (not @member.name) "gh-members-name-noname"}}">{{or @member.name @member.email}}</h3>
{{#if @member.name}}
<p class="ma0 pa0 middarkgrey f8 gh-members-list-email">{{@member.email}}</p>
{{/if}}
</div>
</div>
</LinkTo>
{{#if (feature "emailAnalytics")}}
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data gh-members-list-open-rate middarkgrey f8 {{if (not @member.name) "gh-members-list-open-rate-noname"}}">
{{#if (not (is-empty @member.emailOpenRate))}}
<span class="gh-members-list-open-rate-mobile">{{@member.emailOpenRate}}%</span>
{{#if (feature "emailAnalytics")}}
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data middarkgrey f8 {{if (not @member.name) "gh-members-list-open-rate-noname"}}">
{{#if (not (is-empty @member.emailOpenRate))}}
<span class="gh-members-list-open-rate-mobile">{{@member.emailOpenRate}}%</span>
{{else}}
<span class="midlightgrey">N/A</span>
{{/if}}
</LinkTo>
{{/if}}
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data middarkgrey f8 {{if (not @member.name) "gh-members-geolocation-noname"}}">
{{#if (and @member.geolocation @member.geolocation.country)}}
{{#if (and (eq @member.geolocation.country_code "US") @member.geolocation.region)}}
{{@member.geolocation.region}}, US
{{else}}
{{#if @member.geolocation.country}}
{{@member.geolocation.country}}
{{else}}
<span class="midlightgrey">Unknown</span>
{{/if}}
{{/if}}
{{else}}
<span class="midlightgrey">Unknown</span>
{{/if}}
</LinkTo>
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data middarkgrey f8">
{{#if @member.createdAtUTC}}
<div>{{moment-format @member.createdAtUTC "D MMM YYYY"}}</div>
<div class="midlightgrey gh-members-list-subscribed-moment">{{moment-from-now @member.createdAtUTC}}</div>
{{/if}}
</LinkTo>
{{#if (feature "emailAnalytics")}}
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data middarkgrey f8">
{{#if (not (is-empty @member.emailOpenRate))}}
<span class="gh-members-list-open-rate-mobile">{{@member.emailOpenRate}}%</span>
{{/if}}
</LinkTo>
{{/if}}
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data middarkgrey f8">
{{#if (and @member.geolocation @member.geolocation.country)}}
{{#if (and (eq @member.geolocation.country_code "US") @member.geolocation.region)}}
{{@member.geolocation.region}}, US
{{else}}
{{#if @member.geolocation.country}}
{{@member.geolocation.country}}
{{else}}
<span class="midlightgrey">Unknown</span>
{{/if}}
{{/if}}
{{else}}
<span class="midlightgrey">Unknown</span>
{{/if}}
</LinkTo>
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data middarkgrey f8">
{{#if @member.createdAtUTC}}
<div>{{moment-format @member.createdAtUTC "D MMM YYYY"}}</div>
<div class="midlightgrey gh-members-list-subscribed-moment">{{moment-from-now @member.createdAtUTC}}</div>
{{/if}}
</LinkTo>
{{#if (feature "emailAnalytics")}}
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data middarkgrey f8">
{{#if (not (is-empty @member.emailOpenRate))}}
<span class="gh-members-list-open-rate-mobile">{{@member.emailOpenRate}}%</span>
{{/if}}
</LinkTo>
{{/if}}
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data middarkgrey f8">
{{#if (and @member.geolocation @member.geolocation.country)}}
{{#if (and (eq @member.geolocation.country_code "US") @member.geolocation.region)}}
{{@member.geolocation.region}}, US
{{else}}
{{#if @member.geolocation.country}}
{{@member.geolocation.country}}
{{else}}
<span class="midlightgrey">Unknown</span>
{{/if}}
{{/if}}
{{else}}
<span class="midlightgrey">Unknown</span>
{{/if}}
</LinkTo>
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data middarkgrey f8">
{{#if @member.createdAtUTC}}
<div>{{moment-format @member.createdAtUTC "D MMM YYYY"}}</div>
<div class="midlightgrey gh-members-list-subscribed-moment">{{moment-from-now @member.createdAtUTC}}</div>
{{/if}}
</LinkTo>
{{/if}}
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data gh-members-list-geolocation middarkgrey f8 {{if (not @member.name) "gh-members-geolocation-noname"}}">
{{#if (and @member.geolocation @member.geolocation.country)}}
{{#if (and (eq @member.geolocation.country_code "US") @member.geolocation.region)}}
{{@member.geolocation.region}}, US
{{else}}
{{#if @member.geolocation.country}}
{{@member.geolocation.country}}
{{else}}
<span class="midlightgrey">Unknown</span>
{{/if}}
{{/if}}
{{else}}
<span class="midlightgrey">Unknown</span>
{{/if}}
</LinkTo>
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data gh-members-list-subscribed-at middarkgrey f8 {{if (not @member.name) "gh-members-subscribed-noname"}}">
{{#if @member.createdAtUTC}}
<div>{{moment-format @member.createdAtUTC "D MMM YYYY"}}</div>
<div class="midlightgrey gh-members-list-subscribed-moment">{{moment-from-now @member.createdAtUTC}}</div>
{{/if}}
</LinkTo>
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data gh-list-cellwidth-chevron gh-members-list-chevron">
<div class="flex items-center justify-end w-100 h-100">
<span class="nr2">{{svg-jar "arrow-right" class="w6 h6 fill-midgrey pa1"}}</span>
</tr>
{{else}}
<li class="gh-list-row gh-members-list-item {{if @member.is_loading "loading"}}" data-test-member={{@member.id}} ...attributes>
{{#if @member.is_loading}}
<div class="gh-list-data gh-members-list-basic gh-list-loadingcell">
<div class="gh-list-loading-title"></div>
<div class="gh-list-loading-detail"></div>
</div>
</LinkTo>
{{/if}}
</li>
<div class="gh-list-data gh-members-list-open-rate gh-list-cellwidth-10"></div>
<div class="gh-list-data gh-members-list-geolocation gh-list-cellwidth-10"></div>
<div class="gh-list-data gh-members-list-subscribed-at gh-list-cellwidth-10"></div>
<div class="gh-list-data gh-members-list-chevron gh-list-cellwidth-chevron"></div>
{{else}}
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data gh-members-list-basic">
<div class="flex items-center">
<GhMemberAvatar @member={{@member}} @containerClass="w9 h9 mr3 flex-shrink-0" />
<div class="w-80">
<h3 class="ma0 pa0 gh-members-list-name {{if (not @member.name) "gh-members-name-noname"}}">{{or @member.name @member.email}}</h3>
{{#if @member.name}}
<p class="ma0 pa0 middarkgrey f8 gh-members-list-email">{{@member.email}}</p>
{{/if}}
</div>
</div>
</LinkTo>
{{#if (feature "emailAnalytics")}}
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data gh-members-list-open-rate middarkgrey f8 {{if (not @member.name) "gh-members-list-open-rate-noname"}}">
{{#if (not (is-empty @member.emailOpenRate))}}
<span class="gh-members-list-open-rate-mobile">{{@member.emailOpenRate}}%</span>
{{/if}}
</LinkTo>
{{/if}}
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data gh-members-list-geolocation middarkgrey f8 {{if (not @member.name) "gh-members-geolocation-noname"}}">
{{#if (and @member.geolocation @member.geolocation.country)}}
{{#if (and (eq @member.geolocation.country_code "US") @member.geolocation.region)}}
{{@member.geolocation.region}}, US
{{else}}
{{#if @member.geolocation.country}}
{{@member.geolocation.country}}
{{else}}
<span class="midlightgrey">Unknown</span>
{{/if}}
{{/if}}
{{else}}
<span class="midlightgrey">Unknown</span>
{{/if}}
</LinkTo>
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data gh-members-list-subscribed-at middarkgrey f8 {{if (not @member.name) "gh-members-subscribed-noname"}}">
{{#if @member.createdAtUTC}}
<div>{{moment-format @member.createdAtUTC "D MMM YYYY"}}</div>
<div class="midlightgrey gh-members-list-subscribed-moment">{{moment-from-now @member.createdAtUTC}}</div>
{{/if}}
</LinkTo>
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data gh-list-cellwidth-chevron gh-members-list-chevron">
<div class="flex items-center justify-end w-100 h-100">
<span class="nr2">{{svg-jar "arrow-right" class="w6 h6 fill-midgrey pa1"}}</span>
</div>
</LinkTo>
{{/if}}
</li>
{{/if}}

View File

@ -295,4 +295,79 @@ ul.nostyle li {
max-width: 320px;
height: 9px;
margin-top: 8px;
}
/* Horizontally scrolling list */
.gh-list-scrolling {
position: relative;
overflow: scroll;
max-width: calc(100% + 96px);
height: calc(100vh - 96px);
margin: 0 -48px -81px;
padding: 0 48px 0 0;
}
.gh-list-scrolling table {
position: relative;
border-collapse: inherit;
margin: 0;
}
.gh-list-scrolling thead th {
position: sticky;
top: 0;
vertical-align: middle;
font-size: 1.1rem;
font-weight: 500;
letter-spacing: 0.1px;
color: var(--black);
background: var(--main-bg-color);
background: linear-gradient(90deg, rgba(255,255,255,1) 90%, rgba(255,255,255,0) 100%);
border-bottom: var(--whitegrey) 1px solid;
padding: 10px 20px;
text-transform: uppercase;
white-space: nowrap;
}
.gh-list-scrolling thead th:first-child {
left: 0;
z-index: 1;
border-bottom: none;
padding: 0 60px 0 48px;
}
.gh-list-scrolling tbody .gh-list-data:first-child {
position: sticky;
left: 0;
border-bottom: none;
background: var(--main-bg-color);
background: linear-gradient(90deg, rgba(255,255,255,1) 90%, rgba(255,255,255,0) 100%);
padding: 0 60px 0 48px;
}
.gh-list-scrolling thead th:first-child::before,
.gh-list-scrolling tbody .gh-list-data:first-child::before {
position: absolute;
content: "";
bottom: 0;
right: 0;
left: 48px;
height: 1px;
background: var(--whitegrey);
}
.gh-list-scrolling td,
.gh-list-scrolling th,
.gh-list-scrolling a {
white-space: nowrap;
}
.gh-list-scrolling tbody th{
position: sticky;
left: 0;
}
.gh-list-scrolling tbody .gh-list-data {
border-top: none;
border-bottom: var(--whitegrey) 1px solid;
}

View File

@ -66,6 +66,10 @@
max-width: 1600px;
}
.gh-main-fullwidth .gh-canvas {
max-width: 100%;
}
/* Flexbox fix. https://github.com/TryGhost/Ghost/issues/5804#issuecomment-141416812 */
.gh-main > section {
width: 100%;

View File

@ -68,35 +68,13 @@
.members-list .gh-list-header {
position: sticky;
top: 84px;
top: 96px;
z-index: 1;
background: var(--white);
}
.members-list-header-overlay {
display: flex;
flex-direction: row;
justify-content: space-between;
position: sticky;
height: 40px;
top: calc(84px - 40px); /* match gh-list-header sticky top */
left: 0;
right: 0;
margin-top: -40px;
transform: translateY(40px);
z-index: 2;
border-radius: 5px 5px 0 0;
border-bottom: 1px solid #e5eff5;
font-size: 1.2rem;
font-weight: 500;
letter-spacing: .1px;
color: #738a94;
text-transform: uppercase;
padding: 10px 16px;
white-space: nowrap;
background: #f8fafc;
.gh-list-scrolling-h .members-list .gh-list-header {
top: 0;
}
.members-header .view-actions input.gh-members-list-searchfield {

View File

@ -8,7 +8,7 @@
<GhNavMenu />
{{/if}}
<main class="gh-main {{this.ui.mainClass}}" role="main">
<main class="gh-main {{this.ui.mainClass}} {{if (and (feature "membersFiltering") (or (eq this.router.currentURL "/members") (eq this.router.currentURL "/members/import"))) "gh-main-fullwidth"}}" role="main">
{{outlet}}
{{#if this.showBilling}}

View File

@ -80,6 +80,32 @@
{{#unless this.members.loading}}
<section class="view-container">
{{#if (feature "membersFiltering")}}
<div class="gh-list-scrolling">
<table class="gh-list">
<thead>
<tr>
<th>{{this.listHeader}}</th>
<th>Open rate</th>
<th>Location</th>
<th>Created</th>
<th>Open rate</th>
<th>Location</th>
<th>Created</th>
<th>Open rate</th>
<th>Location</th>
<th>Created</th>
</tr>
</thead>
<VerticalCollection @tagName="tbody" @items={{this.members}} @key="id" @containerSelector=".gh-main" @estimateHeight={{69}} @staticHeight={{true}} @bufferSize={{20}} as |member|>
<GhMembersListItem
@member={{member}}
data-test-member={{member.id}}
/>
</VerticalCollection>
</table>
</div>
{{else}}
<section class="content-list">
<ol class="members-list gh-list {{unless this.members "no-posts"}}">
{{#if this.members}}
@ -113,6 +139,7 @@
{{/if}}
</ol>
</section>
{{/if}}
</section>
{{else}}
<div class="gh-content">