Added rough "edit mode" for members table behind dev flag

no issue

- initial UI for bulk-selection, focusing for now on first stage which is limited to select-all and delete actions
- adds "Edit" button to the members table that shows the "selection" toolbar in place of the table header and shows a checkbox next to each member
This commit is contained in:
Kevin Ansfield 2020-06-18 11:05:03 +01:00
parent 3e5a8d4e20
commit 0f587a90d0
4 changed files with 121 additions and 8 deletions

View File

@ -1,5 +1,10 @@
<li class="gh-list-row gh-members-list-item {{if @member.is_loading "loading"}}" ...attributes>
{{#if @member.is_loading}}
{{#if @isEditing}}
<div class="gh-list-data gh-members-list-checkbox">
<input type="checkbox" checked={{@isSelected}} disabled>
</div>
{{/if}}
<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>
@ -8,6 +13,12 @@
<div class="gh-list-data gh-members-list-subscribed-at gh-list-cellwidth-20"></div>
<div class="gh-list-data gh-members-list-chevron gh-list-cellwidth-chevron"></div>
{{else}}
{{#if @isEditing}}
<div class="gh-list-data gh-members-list-checkbox">
<input type="checkbox" checked={{@isSelected}} disabled>
</div>
{{/if}}
<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" />
@ -21,7 +32,7 @@
</LinkTo>
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data gh-members-list-geolocation gh-list-cellwidth-20 middarkgrey f8 {{if (not @member.name) "gh-members-geolocation-noname"}}">
{{#if @member.geolocation}}
{{#if (and @member.geolocation @member.geolocation.country)}}
{{#if (eq @member.geolocation.country_code "US")}}
{{@member.geolocation.region}}, US
{{else}}
@ -34,14 +45,17 @@
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data gh-members-list-subscribed-at gh-list-cellwidth-20 middarkgrey f8 {{if (not @member.name) "gh-members-subscribed-noname"}}">
{{#if @member.createdAtUTC}}
{{moment-format @member.createdAtUTC "MMM DD, YYYY"}} <span class="midlightgrey">({{moment-from-now @member.createdAtUTC}})</span>
{{moment-format @member.createdAtUTC "MMM DD, YYYY"}}
<span class="midlightgrey">({{moment-from-now @member.createdAtUTC}})</span>
{{/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>
{{#unless @isEditing}}
<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>
{{/unless}}
</LinkTo>
{{/if}}
</li>

View File

@ -22,6 +22,7 @@ const PAID_PARAMS = [{
}];
export default class MembersController extends Controller {
@service config;
@service ellaSparse;
@service feature;
@service membersStats;
@ -34,11 +35,13 @@ export default class MembersController extends Controller {
];
@tracked members = A([]);
@tracked allSelected = false;
@tracked searchText = '';
@tracked searchParam = '';
@tracked paidParam = null;
@tracked label = null;
@tracked modalLabel = null;
@tracked isEditing = false;
@tracked showLabelModal = false;
@tracked _availableLabels = A([]);
@ -111,6 +114,20 @@ export default class MembersController extends Controller {
return this.paidParams.findBy('value', this.paidParam) || {value: '!unknown'};
}
get selectedCount() {
return this.allSelected ? this.members.length : 0;
}
get selectAllLabel() {
let {members} = this;
if (this.allSelected) {
return `All items selected (${formatNumber(members.length)})`;
} else {
return `Select all (${formatNumber(members.length)})`;
}
}
// Actions -----------------------------------------------------------------
@action
@ -121,6 +138,16 @@ export default class MembersController extends Controller {
this.membersStats.fetch();
}
@action
toggleEditMode() {
this.isEditing = !this.isEditing;
}
@action
toggleSelectAll() {
this.allSelected = !this.allSelected;
}
@action
search(e) {
this.searchTask.perform(e.target.value);
@ -182,6 +209,13 @@ export default class MembersController extends Controller {
this.paidParam = paid.value;
}
@action
confirmDeleteMembers() {
let {members} = this;
let count = `${formatNumber(members.length)} ${pluralize(members.length, 'member', {withoutCount: true})}`;
alert(`Once deletion is implemented, you'll see a confirmation for deleting ${count} here`);
}
// Tasks -------------------------------------------------------------------
@task({restartable: true})

View File

@ -59,6 +59,32 @@
z-index: 1;
}
.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;
}
.members-header .view-actions input.gh-members-list-searchfield {
min-width: 220px;
padding-left: 30px;
@ -78,6 +104,10 @@
box-shadow: inset 0 0 0 1px #3eb0ef;
}
.gh-members-list-checkbox {
width: 36px;
}
p.gh-members-list-email {
margin: -2px 0 -1px;
}

View File

@ -57,16 +57,51 @@
{{/if}}
<section class="content-list">
{{!-- overlaid on header to keep table column sizing --}}
{{#if this.isEditing}}
<div class="members-list-header-overlay">
<div class="flex flex-row">
<div>
<input type="checkbox" id="select-all-members" name="select-all-members" {{on "input" this.toggleSelectAll}}>
<label for="select-all-members">{{this.selectAllLabel}}</label>
</div>
<button class="gh-btn" {{on "click" this.confirmDeleteMembers}} disabled={{not this.selectedCount}}>
<span>Delete</span>
</button>
</div>
<button class="gh-btn" {{on "click" this.toggleEditMode}}>
<span>Done</span>
</button>
</div>
{{/if}}
<ol class="members-list gh-list {{unless this.members "no-posts"}}">
{{#if this.members}}
<li class="gh-list-row header">
<li class="gh-list-row header relative">
{{#if this.isEditing}}
{{!-- necessary because we add an extra column in the list items --}}
<div class="gh-list-header gh-members-list-checkbox"></div>
{{/if}}
<div class="gh-list-header">{{this.listHeader}}</div>
<div class="gh-list-header gh-members-list-geolocation gh-list-cellwidth-20 nowrap">Location</div>
<div class="gh-list-header gh-members-list-subscribed-at gh-list-cellwidth-20 nowrap">Created</div>
<div class="gh-list-header gh-members-list-chevron gh-list-cellwidth-chevron"></div>
<div class="gh-list-header gh-members-list-chevron gh-list-cellwidth-chevron">
{{!-- TODO: 🍆🖌 --}}
{{#if this.config.enableDeveloperExperiments}}
<button class="gh-btn" style="position: absolute; top: 2px; right: 3px" {{on "click" this.toggleEditMode}}>
<span>Edit</span>
</button>
{{/if}}
</div>
</li>
<VerticalCollection @items={{this.members}} @key="id" @containerSelector=".gh-main" @estimateHeight={{69}} @staticHeight={{true}} @bufferSize={{20}} as |member|>
<GhMembersListItem @member={{member}} @data-test-member-id={{member.id}} />
<GhMembersListItem
@member={{member}}
@isEditing={{this.isEditing}}
@isSelected={{this.allSelected}}
@data-test-member-id={{member.id}}
/>
</VerticalCollection>
{{else}}
<li class="no-posts-box">