This reverts commit e206bd8907
.
This commit is contained in:
parent
e204c61bd8
commit
654ba44723
|
@ -15,7 +15,7 @@ module.exports = {
|
||||||
"directory-item-value",
|
"directory-item-value",
|
||||||
"directory-table-header-title",
|
"directory-table-header-title",
|
||||||
"loading-spinner",
|
"loading-spinner",
|
||||||
"directory-item-label",
|
"mobile-directory-item-label",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
"no-implicit-this": {
|
"no-implicit-this": {
|
||||||
|
|
|
@ -31,21 +31,6 @@ export default Controller.extend(CanCheckEmails, {
|
||||||
return I18n.t("admin.users.titles." + query);
|
return I18n.t("admin.users.titles." + query);
|
||||||
},
|
},
|
||||||
|
|
||||||
@discourseComputed("showEmails")
|
|
||||||
columnCount(showEmails) {
|
|
||||||
let colCount = 7; // note that the first column is hardcoded in the template
|
|
||||||
|
|
||||||
if (showEmails) {
|
|
||||||
colCount += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.siteSettings.must_approve_users) {
|
|
||||||
colCount += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return colCount;
|
|
||||||
},
|
|
||||||
|
|
||||||
@observes("listFilter")
|
@observes("listFilter")
|
||||||
_filterUsers() {
|
_filterUsers() {
|
||||||
discourseDebounce(this, this.resetFilters, INPUT_DELAY);
|
discourseDebounce(this, this.resetFilters, INPUT_DELAY);
|
||||||
|
|
|
@ -26,204 +26,140 @@
|
||||||
@title={{this.searchHint}}
|
@title={{this.searchHint}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<LoadMore
|
<LoadMore
|
||||||
@class="users-list-container"
|
@class="users-list-container"
|
||||||
@selector=".users-list tr"
|
@selector=".users-list tr"
|
||||||
@action={{action "loadMore"}}
|
@action={{action "loadMore"}}
|
||||||
>
|
>
|
||||||
{{#if this.model}}
|
{{#if this.model}}
|
||||||
|
<table class="table users-list grid" role="table" aria-label={{this.title}}>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<TableHeaderToggle
|
||||||
|
@field="username"
|
||||||
|
@labelKey="username"
|
||||||
|
@order={{this.order}}
|
||||||
|
@asc={{this.asc}}
|
||||||
|
@automatic={{true}}
|
||||||
|
/>
|
||||||
|
<TableHeaderToggle
|
||||||
|
@class={{if this.showEmails "" "hidden"}}
|
||||||
|
@field="email"
|
||||||
|
@labelKey="email"
|
||||||
|
@order={{this.order}}
|
||||||
|
@asc={{this.asc}}
|
||||||
|
@automatic={{true}}
|
||||||
|
/>
|
||||||
|
<TableHeaderToggle
|
||||||
|
@field="last_emailed"
|
||||||
|
@labelKey="admin.users.last_emailed"
|
||||||
|
@order={{this.order}}
|
||||||
|
@asc={{this.asc}}
|
||||||
|
@automatic={{true}}
|
||||||
|
/>
|
||||||
|
<TableHeaderToggle
|
||||||
|
@field="seen"
|
||||||
|
@labelKey="last_seen"
|
||||||
|
@order={{this.order}}
|
||||||
|
@asc={{this.asc}}
|
||||||
|
@automatic={{true}}
|
||||||
|
/>
|
||||||
|
<TableHeaderToggle
|
||||||
|
@field="topics_viewed"
|
||||||
|
@labelKey="admin.user.topics_entered"
|
||||||
|
@order={{this.order}}
|
||||||
|
@asc={{this.asc}}
|
||||||
|
@automatic={{true}}
|
||||||
|
/>
|
||||||
|
<TableHeaderToggle
|
||||||
|
@field="posts_read"
|
||||||
|
@labelKey="admin.user.posts_read_count"
|
||||||
|
@order={{this.order}}
|
||||||
|
@asc={{this.asc}}
|
||||||
|
@automatic={{true}}
|
||||||
|
/>
|
||||||
|
<TableHeaderToggle
|
||||||
|
@field="read_time"
|
||||||
|
@labelKey="admin.user.time_read"
|
||||||
|
@order={{this.order}}
|
||||||
|
@asc={{this.asc}}
|
||||||
|
@automatic={{true}}
|
||||||
|
/>
|
||||||
|
<TableHeaderToggle
|
||||||
|
@field="created"
|
||||||
|
@labelKey="created"
|
||||||
|
@order={{this.order}}
|
||||||
|
@asc={{this.asc}}
|
||||||
|
@automatic={{true}}
|
||||||
|
/>
|
||||||
|
<PluginOutlet
|
||||||
|
@name="admin-users-list-thead-after"
|
||||||
|
@outletArgs={{hash order=this.order asc=this.asc}}
|
||||||
|
/>
|
||||||
|
|
||||||
<ResponsiveTable
|
{{#if this.siteSettings.must_approve_users}}
|
||||||
@className="users-list"
|
<th>{{i18n "admin.users.approved"}}</th>
|
||||||
@aria-label={{this.title}}
|
{{/if}}
|
||||||
@style={{html-safe
|
<th> </th>
|
||||||
(concat
|
</tr>
|
||||||
"grid-template-columns: minmax(min-content, 2fr) repeat("
|
</thead>
|
||||||
(html-safe this.columnCount)
|
<tbody>
|
||||||
", minmax(min-content, 1fr))"
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
@updates={{this.model.email}}
|
|
||||||
>
|
|
||||||
|
|
||||||
<:header>
|
|
||||||
<TableHeaderToggle
|
|
||||||
@class="directory-table__column-header--username"
|
|
||||||
@field="username"
|
|
||||||
@labelKey="username"
|
|
||||||
@order={{this.order}}
|
|
||||||
@asc={{this.asc}}
|
|
||||||
@automatic={{true}}
|
|
||||||
/>
|
|
||||||
<TableHeaderToggle
|
|
||||||
@class={{if
|
|
||||||
this.showEmails
|
|
||||||
"directory-table__column-header--email"
|
|
||||||
"hidden"
|
|
||||||
}}
|
|
||||||
@field="email"
|
|
||||||
@labelKey="email"
|
|
||||||
@order={{this.order}}
|
|
||||||
@asc={{this.asc}}
|
|
||||||
@automatic={{true}}
|
|
||||||
/>
|
|
||||||
<TableHeaderToggle
|
|
||||||
@field="last_emailed"
|
|
||||||
@labelKey="admin.users.last_emailed"
|
|
||||||
@order={{this.order}}
|
|
||||||
@asc={{this.asc}}
|
|
||||||
@automatic={{true}}
|
|
||||||
/>
|
|
||||||
<TableHeaderToggle
|
|
||||||
@field="seen"
|
|
||||||
@labelKey="last_seen"
|
|
||||||
@order={{this.order}}
|
|
||||||
@asc={{this.asc}}
|
|
||||||
@automatic={{true}}
|
|
||||||
/>
|
|
||||||
<TableHeaderToggle
|
|
||||||
@field="topics_viewed"
|
|
||||||
@labelKey="admin.user.topics_entered"
|
|
||||||
@order={{this.order}}
|
|
||||||
@asc={{this.asc}}
|
|
||||||
@automatic={{true}}
|
|
||||||
/>
|
|
||||||
<TableHeaderToggle
|
|
||||||
@field="posts_read"
|
|
||||||
@labelKey="admin.user.posts_read_count"
|
|
||||||
@order={{this.order}}
|
|
||||||
@asc={{this.asc}}
|
|
||||||
@automatic={{true}}
|
|
||||||
/>
|
|
||||||
<TableHeaderToggle
|
|
||||||
@field="read_time"
|
|
||||||
@labelKey="admin.user.time_read"
|
|
||||||
@order={{this.order}}
|
|
||||||
@asc={{this.asc}}
|
|
||||||
@automatic={{true}}
|
|
||||||
/>
|
|
||||||
<TableHeaderToggle
|
|
||||||
@field="created"
|
|
||||||
@labelKey="created"
|
|
||||||
@order={{this.order}}
|
|
||||||
@asc={{this.asc}}
|
|
||||||
@automatic={{true}}
|
|
||||||
/>
|
|
||||||
<PluginOutlet
|
|
||||||
@name="admin-users-list-thead-after"
|
|
||||||
@outletArgs={{hash order=this.order asc=this.asc}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{{#if this.siteSettings.must_approve_users}}
|
|
||||||
<div class="directory-table__column-header">{{i18n
|
|
||||||
"admin.users.approved"
|
|
||||||
}}</div>
|
|
||||||
{{/if}}
|
|
||||||
<div class="directory-table__column-header"> </div>
|
|
||||||
|
|
||||||
</:header>
|
|
||||||
|
|
||||||
<:body>
|
|
||||||
{{#each this.model as |user|}}
|
{{#each this.model as |user|}}
|
||||||
<div
|
<tr
|
||||||
class="user
|
class="user
|
||||||
{{user.selected}}
|
{{user.selected}}
|
||||||
{{unless user.active 'not-activated'}}
|
{{unless user.active 'not-activated'}}"
|
||||||
directory-table__row"
|
|
||||||
>
|
>
|
||||||
<div class="directory-table__cell username">
|
<td class="username">
|
||||||
<a
|
<a href={{user.path}} data-user-card={{user.username}}>
|
||||||
class="avatar"
|
|
||||||
href={{user.path}}
|
|
||||||
data-user-card={{user.username}}
|
|
||||||
>
|
|
||||||
{{avatar user imageSize="small"}}
|
{{avatar user imageSize="small"}}
|
||||||
</a>
|
</a>
|
||||||
<LinkTo @route="adminUser" @model={{user}}>
|
<LinkTo
|
||||||
{{user.username}}
|
@route="adminUser"
|
||||||
</LinkTo>
|
@model={{user}}
|
||||||
|
>{{user.username}}</LinkTo>
|
||||||
{{#if user.staged}}
|
{{#if user.staged}}
|
||||||
{{d-icon "far-envelope" title="user.staged"}}
|
{{d-icon "far-envelope" title="user.staged"}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</td>
|
||||||
<div
|
<td class="email {{if this.showEmails '' 'hidden'}}">
|
||||||
class="directory-table__cell email
|
{{~user.email~}}
|
||||||
{{if this.showEmails '' 'hidden'}}"
|
</td>
|
||||||
>
|
|
||||||
<span class="directory-table__value">
|
|
||||||
{{~user.email~}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#if user.last_emailed_at}}
|
{{#if user.last_emailed_at}}
|
||||||
<div
|
<td class="last-emailed" title={{raw-date user.last_emailed_at}}>
|
||||||
class="directory-table__cell last-emailed"
|
<div class="label">{{i18n "admin.users.last_emailed"}}</div>
|
||||||
title={{raw-date user.last_emailed_at}}
|
<div>{{format-duration user.last_emailed_age}}</div>
|
||||||
>
|
</td>
|
||||||
<span class="directory-table__label">
|
|
||||||
<span>{{i18n "admin.users.last_emailed"}}</span>
|
|
||||||
</span>
|
|
||||||
<span class="directory-table__value">
|
|
||||||
{{format-duration user.last_emailed_age}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="directory-table__cell last-emailed">
|
<td class="last-emailed">
|
||||||
<span class="directory-table__label">
|
<div class="label">{{i18n "admin.users.last_emailed"}}</div>
|
||||||
<span>{{i18n "admin.users.last_emailed"}}</span>
|
<div>{{format-duration user.last_emailed_age}}</div>
|
||||||
</span>
|
</td>
|
||||||
<span class="directory-table__value">
|
|
||||||
{{format-duration user.last_emailed_age}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<div
|
<td class="last-seen" title={{raw-date user.last_seen_at}}>
|
||||||
class="directory-table__cell last-seen"
|
<div class="label">{{i18n "last_seen"}}</div>
|
||||||
title={{raw-date user.last_seen_at}}
|
<div>{{format-duration user.last_seen_age}}</div>
|
||||||
>
|
</td>
|
||||||
<span class="directory-table__label">
|
<td class="topics-entered">
|
||||||
<span>{{i18n "last_seen"}}</span>
|
<div class="label">{{i18n "admin.user.topics_entered"}}</div>
|
||||||
</span>
|
<div>{{number user.topics_entered}}</div>
|
||||||
<span class="directory-table__value">
|
</td>
|
||||||
{{format-duration user.last_seen_age}}
|
<td class="posts-read">
|
||||||
</span>
|
<div class="label">{{i18n "admin.user.posts_read_count"}}</div>
|
||||||
</div>
|
<div>{{number user.posts_read_count}}</div>
|
||||||
<div class="directory-table__cell topics-entered">
|
</td>
|
||||||
<span class="directory-table__label">
|
<td class="time-read">
|
||||||
<span>{{i18n "admin.user.topics_entered"}}</span>
|
<div class="label">{{i18n "admin.user.time_read"}}</div>
|
||||||
</span>
|
<div>{{format-duration user.time_read}}</div>
|
||||||
<span class="directory-table__value">
|
</td>
|
||||||
{{number user.topics_entered}}
|
|
||||||
</span>
|
<td class="created" title={{raw-date user.created_at}}>
|
||||||
</div>
|
<div class="label">{{i18n "created"}}</div>
|
||||||
<div class="directory-table__cell posts-read">
|
<div>{{format-duration user.created_at_age}}</div>
|
||||||
<span class="directory-table__label">
|
</td>
|
||||||
<span>{{i18n "admin.user.posts_read_count"}}</span>
|
|
||||||
</span>
|
|
||||||
<span class="directory-table__value">
|
|
||||||
{{number user.posts_read_count}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="directory-table__cell time-read">
|
|
||||||
<span class="directory-table__label">
|
|
||||||
<span>{{i18n "admin.user.time_read"}}</span>
|
|
||||||
</span>
|
|
||||||
<span class="directory-table__value">
|
|
||||||
{{format-duration user.time_read}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="directory-table__cell created"
|
|
||||||
title={{raw-date user.created_at}}
|
|
||||||
>
|
|
||||||
<span class="directory-table__label">
|
|
||||||
<span>{{i18n "created"}}</span>
|
|
||||||
</span>
|
|
||||||
<span class="directory-table__value">
|
|
||||||
{{format-duration user.created_at_age}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<PluginOutlet
|
<PluginOutlet
|
||||||
@name="admin-users-list-td-after"
|
@name="admin-users-list-td-after"
|
||||||
|
@ -231,43 +167,29 @@
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{{#if this.siteSettings.must_approve_users}}
|
{{#if this.siteSettings.must_approve_users}}
|
||||||
<div class="directory-table__cell">
|
<td>{{i18n-yes-no user.approved}}</td>
|
||||||
<span class="directory-table__label">
|
|
||||||
<span>{{i18n "admin.users.approved"}}</span>
|
|
||||||
</span>
|
|
||||||
<span class="directory-table__value">
|
|
||||||
{{i18n-yes-no user.approved}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<div class="directory-table__cell user-status">
|
<td class="user-status">
|
||||||
<span class="directory-table__label">
|
{{#if user.admin}}
|
||||||
<span>{{i18n "admin.users.status"}}</span>
|
{{d-icon "shield-alt" title="admin.title"}}
|
||||||
</span>
|
{{/if}}
|
||||||
<span class="directory-table__value">
|
{{#if user.moderator}}
|
||||||
{{#if user.admin}}
|
{{d-icon "shield-alt" title="admin.moderator"}}
|
||||||
{{d-icon "shield-alt" title="admin.title"}}
|
{{/if}}
|
||||||
{{/if}}
|
{{#if user.second_factor_enabled}}
|
||||||
{{#if user.moderator}}
|
{{d-icon "lock" title="admin.user.second_factor_enabled"}}
|
||||||
{{d-icon "shield-alt" title="admin.moderator"}}
|
{{/if}}
|
||||||
{{/if}}
|
|
||||||
{{#if user.second_factor_enabled}}
|
|
||||||
{{d-icon "lock" title="admin.user.second_factor_enabled"}}
|
|
||||||
{{/if}}
|
|
||||||
</span>
|
|
||||||
<PluginOutlet
|
<PluginOutlet
|
||||||
@name="admin-users-list-icon"
|
@name="admin-users-list-icon"
|
||||||
@connectorTagName="div"
|
@connectorTagName="div"
|
||||||
@outletArgs={{hash user=user query=this.query}}
|
@outletArgs={{hash user=user query=this.query}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</td>
|
||||||
</div>
|
</tr>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</:body>
|
</tbody>
|
||||||
|
</table>
|
||||||
</ResponsiveTable>
|
|
||||||
|
|
||||||
<ConditionalLoadingSpinner @condition={{this.refreshing}} />
|
<ConditionalLoadingSpinner @condition={{this.refreshing}} />
|
||||||
{{else}}
|
{{else}}
|
||||||
<p>{{i18n "search.no_results"}}</p>
|
<p>{{i18n "search.no_results"}}</p>
|
||||||
|
|
|
@ -1,38 +1,16 @@
|
||||||
<div class="directory-table__cell">
|
<td><UserInfo @user={{this.item.user}} /></td>
|
||||||
<UserInfo @user={{this.item.user}} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#each this.columns as |column|}}
|
{{#each this.columns as |column|}}
|
||||||
{{#if (directory-column-is-user-field column=column)}}
|
<td>
|
||||||
<div class="directory-table__cell--user-field">
|
{{#if (directory-column-is-user-field column=column)}}
|
||||||
<span class="directory-table__label">
|
|
||||||
<span>{{column.name}}</span>
|
|
||||||
</span>
|
|
||||||
{{directory-item-user-field-value item=this.item column=column}}
|
{{directory-item-user-field-value item=this.item column=column}}
|
||||||
</div>
|
{{else}}
|
||||||
{{else}}
|
|
||||||
<div class="directory-table__cell">
|
|
||||||
<span class="directory-table__label">
|
|
||||||
<span>
|
|
||||||
{{#if column.icon}}
|
|
||||||
{{d-icon column.icon}}
|
|
||||||
{{/if}}
|
|
||||||
{{directory-item-label item=this.item column=column}}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
{{directory-item-value item=this.item column=column}}
|
{{directory-item-value item=this.item column=column}}
|
||||||
</div>
|
{{/if}}
|
||||||
{{/if}}
|
</td>
|
||||||
|
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
||||||
{{#if this.showTimeRead}}
|
{{#if this.showTimeRead}}
|
||||||
<div class="directory-table__cell time-read">
|
<td><span class="time-read">{{format-duration
|
||||||
<span class="directory-table__label">
|
this.item.time_read
|
||||||
<span>{{i18n "directory.time_read"}}</span>
|
}}</span></td>
|
||||||
</span>
|
|
||||||
<span class="directory-table__value">
|
|
||||||
{{format-duration this.item.time_read}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
{{/if}}
|
|
@ -2,8 +2,7 @@ import Component from "@ember/component";
|
||||||
import { propertyEqual } from "discourse/lib/computed";
|
import { propertyEqual } from "discourse/lib/computed";
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
tagName: "div",
|
tagName: "tr",
|
||||||
classNames: ["directory-table__row"],
|
|
||||||
classNameBindings: ["me"],
|
classNameBindings: ["me"],
|
||||||
me: propertyEqual("item.user.id", "currentUser.id"),
|
me: propertyEqual("item.user.id", "currentUser.id"),
|
||||||
columns: null,
|
columns: null,
|
||||||
|
|
|
@ -1,37 +1,39 @@
|
||||||
<ResponsiveTable>
|
<div class="directory-table-top-scroll">
|
||||||
<:header>
|
<div class="directory-table-top-scroll-fake-content"></div>
|
||||||
<TableHeaderToggle
|
</div>
|
||||||
@field="username"
|
|
||||||
@order={{this.order}}
|
<div class="directory-table-container">
|
||||||
@asc={{this.asc}}
|
<table class="directory-table">
|
||||||
/>
|
<thead>
|
||||||
{{#each this.columns as |column|}}
|
|
||||||
<TableHeaderToggle
|
<TableHeaderToggle
|
||||||
@field={{column.name}}
|
@field="username"
|
||||||
@icon={{column.icon}}
|
|
||||||
@order={{this.order}}
|
@order={{this.order}}
|
||||||
@asc={{this.asc}}
|
@asc={{this.asc}}
|
||||||
@automatic={{directory-column-is-automatic column=column}}
|
|
||||||
@translated={{column.user_field_id}}
|
|
||||||
@onActiveRender={{this.setActiveHeader}}
|
|
||||||
/>
|
/>
|
||||||
{{/each}}
|
{{#each this.columns as |column|}}
|
||||||
|
<TableHeaderToggle
|
||||||
|
@field={{column.name}}
|
||||||
|
@icon={{column.icon}}
|
||||||
|
@order={{this.order}}
|
||||||
|
@asc={{this.asc}}
|
||||||
|
@automatic={{directory-column-is-automatic column=column}}
|
||||||
|
@translated={{column.user_field_id}}
|
||||||
|
@onActiveRender={{this.setActiveHeader}}
|
||||||
|
/>
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
{{#if this.showTimeRead}}
|
{{#if this.showTimeRead}}
|
||||||
<div class="directory-table__column-header">
|
<th>{{i18n "directory.time_read"}}</th>
|
||||||
<div class="header-contents">
|
{{/if}}
|
||||||
{{i18n "directory.time_read"}}
|
</thead>
|
||||||
</div>
|
<tbody>
|
||||||
</div>
|
{{#each this.items as |item|}}
|
||||||
{{/if}}
|
<DirectoryItem
|
||||||
</:header>
|
@item={{item}}
|
||||||
<:body>
|
@columns={{this.columns}}
|
||||||
{{#each this.items as |item|}}
|
@showTimeRead={{this.showTimeRead}}
|
||||||
<DirectoryItem
|
/>
|
||||||
@item={{item}}
|
{{/each}}
|
||||||
@columns={{this.columns}}
|
</tbody>
|
||||||
@showTimeRead={{this.showTimeRead}}
|
</table>
|
||||||
/>
|
</div>
|
||||||
{{/each}}
|
|
||||||
</:body>
|
|
||||||
</ResponsiveTable>
|
|
|
@ -2,31 +2,109 @@ import Component from "@ember/component";
|
||||||
import { action } from "@ember/object";
|
import { action } from "@ember/object";
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
|
lastScrollPosition: 0,
|
||||||
|
ticking: false,
|
||||||
|
_topHorizontalScrollBar: null,
|
||||||
|
_tableContainer: null,
|
||||||
_table: null,
|
_table: null,
|
||||||
|
_fakeScrollContent: null,
|
||||||
|
|
||||||
didInsertElement() {
|
didInsertElement() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
|
_tableContainer: this.element.querySelector(".directory-table-container"),
|
||||||
|
_topHorizontalScrollBar: this.element.querySelector(
|
||||||
|
".directory-table-top-scroll"
|
||||||
|
),
|
||||||
|
_fakeScrollContent: this.element.querySelector(
|
||||||
|
".directory-table-top-scroll-fake-content"
|
||||||
|
),
|
||||||
_table: this.element.querySelector(".directory-table"),
|
_table: this.element.querySelector(".directory-table"),
|
||||||
_columnCount: this.showTimeRead
|
|
||||||
? this.attrs.columns.value.length + 1
|
|
||||||
: this.attrs.columns.value.length,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this._table.style.gridTemplateColumns = `minmax(13em, 3fr) repeat(${this._columnCount}, minmax(max-content, 1fr))`;
|
this._tableContainer.addEventListener("scroll", this.onBottomScroll);
|
||||||
|
this._topHorizontalScrollBar.addEventListener("scroll", this.onTopScroll);
|
||||||
|
|
||||||
|
// Set active header might have already scrolled the _tableContainer.
|
||||||
|
// Call onHorizontalScroll manually to scroll the _topHorizontalScrollBar
|
||||||
|
this.onResize();
|
||||||
|
this.onHorizontalScroll(this._tableContainer, this._topHorizontalScrollBar);
|
||||||
|
window.addEventListener("resize", this.onResize);
|
||||||
|
},
|
||||||
|
|
||||||
|
@action
|
||||||
|
onResize() {
|
||||||
|
if (
|
||||||
|
this._tableContainer.getBoundingClientRect().bottom < window.innerHeight
|
||||||
|
) {
|
||||||
|
// Bottom of the table is visible. Hide the scrollbar
|
||||||
|
this._fakeScrollContent.style.height = 0;
|
||||||
|
} else {
|
||||||
|
this._fakeScrollContent.style.width = `${this._table.offsetWidth}px`;
|
||||||
|
this._fakeScrollContent.style.height = "1px";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
@action
|
||||||
|
onTopScroll() {
|
||||||
|
this.onHorizontalScroll(this._topHorizontalScrollBar, this._tableContainer);
|
||||||
|
},
|
||||||
|
|
||||||
|
@action
|
||||||
|
onBottomScroll() {
|
||||||
|
this.onHorizontalScroll(this._tableContainer, this._topHorizontalScrollBar);
|
||||||
|
},
|
||||||
|
|
||||||
|
@action
|
||||||
|
onHorizontalScroll(primary, replica) {
|
||||||
|
if (
|
||||||
|
this.isDestroying ||
|
||||||
|
this.isDestroyed ||
|
||||||
|
this.lastScrollPosition === primary.scrollLeft
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.set("lastScrollPosition", primary.scrollLeft);
|
||||||
|
|
||||||
|
if (!this.ticking) {
|
||||||
|
window.requestAnimationFrame(() => {
|
||||||
|
if (!this.isDestroying && !this.isDestroyed) {
|
||||||
|
replica.scrollLeft = this.lastScrollPosition;
|
||||||
|
this.set("ticking", false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.set("ticking", true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
willDestroyElement() {
|
||||||
|
this._tableContainer.removeEventListener("scroll", this.onBottomScroll);
|
||||||
|
this._topHorizontalScrollBar.removeEventListener(
|
||||||
|
"scroll",
|
||||||
|
this.onTopScroll
|
||||||
|
);
|
||||||
|
window.removeEventListener("resize", this.onResize);
|
||||||
},
|
},
|
||||||
|
|
||||||
@action
|
@action
|
||||||
setActiveHeader(header) {
|
setActiveHeader(header) {
|
||||||
// After render, scroll table left to ensure the order by column is visible
|
// After render, scroll table left to ensure the order by column is visible
|
||||||
if (!this._table) {
|
if (!this._tableContainer) {
|
||||||
this.set("_table", document.querySelector(".directory-table"));
|
this.set(
|
||||||
|
"_tableContainer",
|
||||||
|
document.querySelector(".directory-table-container")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
const scrollPixels =
|
const scrollPixels =
|
||||||
header.offsetLeft + header.offsetWidth + 10 - this._table.offsetWidth;
|
header.offsetLeft +
|
||||||
|
header.offsetWidth +
|
||||||
|
10 -
|
||||||
|
this._tableContainer.offsetWidth;
|
||||||
|
|
||||||
if (scrollPixels > 0) {
|
if (scrollPixels > 0) {
|
||||||
this._table.scrollLeft = scrollPixels;
|
this._tableContainer.scrollLeft = scrollPixels;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
<div class="directory-table-top-scroll" {{on "scroll" this.onTopScroll}}>
|
|
||||||
<div class="directory-table-top-scroll-fake-content"></div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class={{concat-class "directory-table" @className}}
|
|
||||||
role="table"
|
|
||||||
aria-label={{@ariaLabel}}
|
|
||||||
style={{@style}}
|
|
||||||
{{did-insert this.checkScroll}}
|
|
||||||
{{did-update this.checkScroll @updates}}
|
|
||||||
{{on-resize this.checkScroll}}
|
|
||||||
{{on "scroll" this.onBottomScroll}}
|
|
||||||
>
|
|
||||||
<div class="directory-table__header">
|
|
||||||
{{yield to="header"}}
|
|
||||||
</div>
|
|
||||||
<div class="directory-table__body">
|
|
||||||
{{yield to="body"}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,51 +0,0 @@
|
||||||
import Component from "@ember/component";
|
|
||||||
import { bind } from "discourse-common/utils/decorators";
|
|
||||||
import { tracked } from "@glimmer/tracking";
|
|
||||||
|
|
||||||
export default class ResponsiveTable extends Component {
|
|
||||||
@tracked lastScrollPosition = 0;
|
|
||||||
@tracked ticking = false;
|
|
||||||
@tracked _table = document.querySelector(".directory-table");
|
|
||||||
@tracked _topHorizontalScrollBar = document.querySelector(
|
|
||||||
".directory-table-top-scroll"
|
|
||||||
);
|
|
||||||
|
|
||||||
@bind
|
|
||||||
checkScroll() {
|
|
||||||
const _fakeScrollContent = document.querySelector(
|
|
||||||
".directory-table-top-scroll-fake-content"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (this._table.getBoundingClientRect().bottom < window.innerHeight) {
|
|
||||||
// Bottom of the table is visible. Hide the scrollbar
|
|
||||||
_fakeScrollContent.style.height = 0;
|
|
||||||
} else {
|
|
||||||
_fakeScrollContent.style.width = `${this._table.scrollWidth}px`;
|
|
||||||
_fakeScrollContent.style.height = "1px";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@bind
|
|
||||||
onTopScroll() {
|
|
||||||
this.onHorizontalScroll(this._topHorizontalScrollBar, this._table);
|
|
||||||
}
|
|
||||||
|
|
||||||
@bind
|
|
||||||
onBottomScroll() {
|
|
||||||
this.onHorizontalScroll(this._table, this._topHorizontalScrollBar);
|
|
||||||
}
|
|
||||||
|
|
||||||
@bind
|
|
||||||
onHorizontalScroll(primary, replica) {
|
|
||||||
this.set("lastScrollPosition", primary?.scrollLeft);
|
|
||||||
|
|
||||||
if (!this.ticking) {
|
|
||||||
window.requestAnimationFrame(() => {
|
|
||||||
replica.scrollLeft = this.lastScrollPosition;
|
|
||||||
this.set("ticking", false);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.set("ticking", true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
<div
|
<span
|
||||||
class="header-contents"
|
class="header-contents"
|
||||||
id={{this.id}}
|
id={{this.id}}
|
||||||
role="button"
|
role="button"
|
||||||
|
@ -6,15 +6,11 @@
|
||||||
aria-label={{this.ariaLabel}}
|
aria-label={{this.ariaLabel}}
|
||||||
aria-pressed={{this.pressedState}}
|
aria-pressed={{this.pressedState}}
|
||||||
>
|
>
|
||||||
|
{{directory-table-header-title
|
||||||
{{yield}}
|
field=this.field
|
||||||
<span class="text">
|
labelKey=this.labelKey
|
||||||
{{directory-table-header-title
|
icon=this.icon
|
||||||
field=this.field
|
translated=this.translated
|
||||||
labelKey=this.labelKey
|
}}
|
||||||
icon=this.icon
|
{{this.chevronIcon}}
|
||||||
translated=this.translated
|
</span>
|
||||||
}}
|
|
||||||
{{this.chevronIcon}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
|
@ -6,8 +6,8 @@ import discourseComputed from "discourse-common/utils/decorators";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
tagName: "div",
|
tagName: "th",
|
||||||
classNames: ["directory-table__column-header", "sortable"],
|
classNames: ["sortable"],
|
||||||
attributeBindings: ["title", "colspan", "ariaSort:aria-sort", "role"],
|
attributeBindings: ["title", "colspan", "ariaSort:aria-sort", "role"],
|
||||||
role: "columnheader",
|
role: "columnheader",
|
||||||
labelKey: null,
|
labelKey: null,
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { number } from "discourse/lib/formatter";
|
||||||
import { registerUnbound } from "discourse-common/lib/helpers";
|
import { registerUnbound } from "discourse-common/lib/helpers";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
|
|
||||||
registerUnbound("directory-item-label", function (args) {
|
registerUnbound("mobile-directory-item-label", function (args) {
|
||||||
// Args should include key/values { item, column }
|
// Args should include key/values { item, column }
|
||||||
const count = args.item.get(args.column.name);
|
const count = args.item.get(args.column.name);
|
||||||
const translationPrefix =
|
const translationPrefix =
|
||||||
|
@ -14,9 +14,7 @@ registerUnbound("directory-item-label", function (args) {
|
||||||
registerUnbound("directory-item-value", function (args) {
|
registerUnbound("directory-item-value", function (args) {
|
||||||
// Args should include key/values { item, column }
|
// Args should include key/values { item, column }
|
||||||
return htmlSafe(
|
return htmlSafe(
|
||||||
`<span class='directory-table__value'>${number(
|
`<span class='number'>${number(args.item.get(args.column.name))}</span>`
|
||||||
args.item.get(args.column.name)
|
|
||||||
)}</span>`
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -27,9 +25,7 @@ registerUnbound("directory-item-user-field-value", function (args) {
|
||||||
? args.item.user.user_fields[args.column.user_field_id]
|
? args.item.user.user_fields[args.column.user_field_id]
|
||||||
: null;
|
: null;
|
||||||
const content = value || "-";
|
const content = value || "-";
|
||||||
return htmlSafe(
|
return htmlSafe(`<span class='user-field-value'>${content}</span>`);
|
||||||
`<span class='directory-table__value--user-field'>${content}</span>`
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
registerUnbound("directory-column-is-automatic", function (args) {
|
registerUnbound("directory-column-is-automatic", function (args) {
|
||||||
|
|
|
@ -1,15 +1,5 @@
|
||||||
<section class="user-content">
|
<section class="user-content">
|
||||||
<div class="group-members-actions">
|
<div class="group-members-actions">
|
||||||
|
|
||||||
{{#if this.canManageGroup}}
|
|
||||||
<DButton
|
|
||||||
@class="bulk-select"
|
|
||||||
@icon="list"
|
|
||||||
@action={{action "toggleBulkSelect"}}
|
|
||||||
@title="topics.bulk.toggle"
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if this.model.can_see_members}}
|
{{#if this.model.can_see_members}}
|
||||||
<TextField
|
<TextField
|
||||||
@value={{this.filterInput}}
|
@value={{this.filterInput}}
|
||||||
|
@ -20,35 +10,6 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if this.canManageGroup}}
|
{{#if this.canManageGroup}}
|
||||||
|
|
||||||
{{#if this.isBulk}}
|
|
||||||
<span class="bulk-select-buttons-wrap">
|
|
||||||
{{#if this.bulkSelection}}
|
|
||||||
<BulkGroupMemberDropdown
|
|
||||||
@bulkSelection={{this.bulkSelection}}
|
|
||||||
@canAdminGroup={{this.model.can_admin_group}}
|
|
||||||
@canEditGroup={{this.model.can_edit_group}}
|
|
||||||
@onChange={{action "actOnSelection" this.bulkSelection}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<DButton
|
|
||||||
@action={{action "bulkClearAll"}}
|
|
||||||
@label="topics.bulk.clear_all"
|
|
||||||
@icon="far-square"
|
|
||||||
@class="bulk-select-clear"
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<DButton
|
|
||||||
@action={{action "bulkSelectAll"}}
|
|
||||||
@label="topics.bulk.select_all"
|
|
||||||
@icon="check-square"
|
|
||||||
@class="bulk-select-all"
|
|
||||||
/>
|
|
||||||
|
|
||||||
</span>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<div class="group-members-manage">
|
<div class="group-members-manage">
|
||||||
<DButton
|
<DButton
|
||||||
@icon="plus"
|
@icon="plus"
|
||||||
|
@ -70,147 +31,134 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if this.hasMembers}}
|
{{#if this.hasMembers}}
|
||||||
<LoadMore
|
<LoadMore @selector=".group-members tr" @action={{action "loadMore"}}>
|
||||||
@selector=".group-members .directory-table-row"
|
<table
|
||||||
@action={{action "loadMore"}}
|
class={{if this.isBulk "group-members sticky-header" "group-members"}}
|
||||||
>
|
|
||||||
|
|
||||||
<ResponsiveTable
|
|
||||||
@className="group-members
|
|
||||||
{{if this.isBulk 'sticky-header' ''}}
|
|
||||||
{{if this.canManageGroup 'group-members--can-manage' ''}}"
|
|
||||||
>
|
>
|
||||||
<:header>
|
<thead>
|
||||||
<TableHeaderToggle
|
<tr>
|
||||||
@order={{this.order}}
|
<th class="bulk-select">
|
||||||
@asc={{this.asc}}
|
{{#if this.canManageGroup}}
|
||||||
@field="username_lower"
|
<FlatButton
|
||||||
@labelKey="username"
|
@class="bulk-select"
|
||||||
@class="username"
|
@icon="list"
|
||||||
@automatic={{true}}
|
@action={{action "toggleBulkSelect"}}
|
||||||
@colspan="2"
|
@title="topics.bulk.toggle"
|
||||||
/>
|
/>
|
||||||
|
{{/if}}
|
||||||
{{#if this.canManageGroup}}
|
</th>
|
||||||
<div class="directory-table__column-header"></div>
|
{{#if this.isBulk}}
|
||||||
{{/if}}
|
<th class="bulk-select-buttons">
|
||||||
|
<span class="bulk-select-buttons-wrap">
|
||||||
<TableHeaderToggle
|
{{#if this.bulkSelection}}
|
||||||
@class="directory-table__column-header"
|
<BulkGroupMemberDropdown
|
||||||
@order={{this.order}}
|
@bulkSelection={{this.bulkSelection}}
|
||||||
@asc={{this.asc}}
|
@canAdminGroup={{this.model.can_admin_group}}
|
||||||
@field="added_at"
|
@canEditGroup={{this.model.can_edit_group}}
|
||||||
@labelKey="groups.member_added"
|
@onChange={{action "actOnSelection" this.bulkSelection}}
|
||||||
@automatic={{true}}
|
|
||||||
/>
|
|
||||||
<TableHeaderToggle
|
|
||||||
@class="directory-table__column-header"
|
|
||||||
@order={{this.order}}
|
|
||||||
@asc={{this.asc}}
|
|
||||||
@field="last_posted_at"
|
|
||||||
@labelKey="last_post"
|
|
||||||
@automatic={{true}}
|
|
||||||
/>
|
|
||||||
<TableHeaderToggle
|
|
||||||
@class="directory-table__column-header"
|
|
||||||
@order={{this.order}}
|
|
||||||
@asc={{this.asc}}
|
|
||||||
@field="last_seen_at"
|
|
||||||
@labelKey="last_seen"
|
|
||||||
@automatic={{true}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{{#if this.canManageGroup}}
|
|
||||||
<div class="directory-table__column-header"></div>
|
|
||||||
{{/if}}
|
|
||||||
</:header>
|
|
||||||
|
|
||||||
<:body>
|
|
||||||
{{#each this.model.members as |m|}}
|
|
||||||
<div class="directory-table__row">
|
|
||||||
|
|
||||||
<div class="directory-table__cell group-member" colspan="2">
|
|
||||||
{{#if this.canManageGroup}}
|
|
||||||
{{#if this.isBulk}}
|
|
||||||
<Input
|
|
||||||
@type="checkbox"
|
|
||||||
class="bulk-select"
|
|
||||||
{{on "click" (action "selectMember" m)}}
|
|
||||||
/>
|
/>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
<DButton
|
||||||
|
@action={{action "bulkSelectAll"}}
|
||||||
|
@label="topics.bulk.select_all"
|
||||||
|
/>
|
||||||
|
<DButton
|
||||||
|
@action={{action "bulkClearAll"}}
|
||||||
|
@label="topics.bulk.clear_all"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</th>
|
||||||
|
{{/if}}
|
||||||
|
<TableHeaderToggle
|
||||||
|
@order={{this.order}}
|
||||||
|
@asc={{this.asc}}
|
||||||
|
@field="username_lower"
|
||||||
|
@labelKey="username"
|
||||||
|
@class="username"
|
||||||
|
@automatic={{true}}
|
||||||
|
@colspan="2"
|
||||||
|
/>
|
||||||
|
<TableHeaderToggle
|
||||||
|
@order={{this.order}}
|
||||||
|
@asc={{this.asc}}
|
||||||
|
@field="added_at"
|
||||||
|
@labelKey="groups.member_added"
|
||||||
|
@automatic={{true}}
|
||||||
|
/>
|
||||||
|
<TableHeaderToggle
|
||||||
|
@order={{this.order}}
|
||||||
|
@asc={{this.asc}}
|
||||||
|
@field="last_posted_at"
|
||||||
|
@labelKey="last_post"
|
||||||
|
@automatic={{true}}
|
||||||
|
/>
|
||||||
|
<TableHeaderToggle
|
||||||
|
@order={{this.order}}
|
||||||
|
@asc={{this.asc}}
|
||||||
|
@field="last_seen_at"
|
||||||
|
@labelKey="last_seen"
|
||||||
|
@automatic={{true}}
|
||||||
|
/>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
{{#each this.model.members as |m|}}
|
||||||
|
<tr>
|
||||||
|
{{#if this.isBulk}}
|
||||||
|
<td class="bulk-select">
|
||||||
|
<Input
|
||||||
|
@type="checkbox"
|
||||||
|
class="bulk-select"
|
||||||
|
{{on "click" (action "selectMember" m)}}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<td class="group-member" colspan="2">
|
||||||
<UserInfo
|
<UserInfo
|
||||||
@user={{m}}
|
@user={{m}}
|
||||||
@skipName={{this.skipName}}
|
@skipName={{this.skipName}}
|
||||||
@showStatus={{true}}
|
@showStatus={{true}}
|
||||||
@showStatusTooltip={{true}}
|
@showStatusTooltip={{true}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</td>
|
||||||
|
|
||||||
{{#if this.canManageGroup}}
|
<td class="group-owner">
|
||||||
<div class="directory-table__cell group-owner">
|
{{#if m.owner}}
|
||||||
{{#if (or m.owner m.primary)}}
|
{{d-icon "shield-alt"}}
|
||||||
<span class="directory-table__label">
|
{{i18n "groups.members.owner"}}<br />
|
||||||
<span>{{i18n "groups.members.status"}}</span>
|
{{/if}}
|
||||||
</span>
|
{{#if m.primary}}
|
||||||
{{/if}}
|
{{i18n "groups.members.primary"}}
|
||||||
<span class="directory-table__value">
|
{{/if}}
|
||||||
{{#if m.owner}}
|
</td>
|
||||||
{{d-icon "shield-alt"}}
|
<td>
|
||||||
{{i18n "groups.members.owner"}}<br />
|
<span class="text">{{bound-date m.added_at}}</span>
|
||||||
{{/if}}
|
</td>
|
||||||
{{#if m.primary}}
|
<td>
|
||||||
{{i18n "groups.members.primary"}}
|
<span class="text">{{bound-date m.last_posted_at}}</span>
|
||||||
{{/if}}
|
</td>
|
||||||
</span>
|
<td>
|
||||||
|
<span class="text">{{bound-date m.last_seen_at}}</span>
|
||||||
|
</td>
|
||||||
|
|
||||||
</div>
|
<td>
|
||||||
{{/if}}
|
{{#if this.canManageGroup}}
|
||||||
<div class="directory-table__cell">
|
|
||||||
<span class="directory-table__label">
|
|
||||||
<span>{{i18n "groups.member_added"}}</span>
|
|
||||||
</span>
|
|
||||||
<span class="directory-table__value">
|
|
||||||
{{bound-date m.added_at}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="directory-table__cell">
|
|
||||||
{{#if m.last_posted_at}}
|
|
||||||
<span class="directory-table__label">
|
|
||||||
<span>{{i18n "last_post"}}</span>
|
|
||||||
</span>
|
|
||||||
{{/if}}
|
|
||||||
<span class="directory-table__value">
|
|
||||||
{{bound-date m.last_posted_at}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="directory-table__cell">
|
|
||||||
{{#if m.last_seen_at}}
|
|
||||||
<span class="directory-table__label">
|
|
||||||
<span>{{i18n "last_seen"}}</span>
|
|
||||||
</span>
|
|
||||||
{{/if}}
|
|
||||||
<span class="directory-table__value">
|
|
||||||
{{bound-date m.last_seen_at}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{{#if this.canManageGroup}}
|
|
||||||
<div class="directory-table__cell member-settings">
|
|
||||||
<GroupMemberDropdown
|
<GroupMemberDropdown
|
||||||
@member={{m}}
|
@member={{m}}
|
||||||
@canAdminGroup={{this.model.can_admin_group}}
|
@canAdminGroup={{this.model.can_admin_group}}
|
||||||
@canEditGroup={{this.model.can_edit_group}}
|
@canEditGroup={{this.model.can_edit_group}}
|
||||||
@onChange={{action "actOnGroup" m}}
|
@onChange={{action "actOnGroup" m}}
|
||||||
/>
|
/>
|
||||||
{{! group parameter is used by plugins }}
|
{{/if}}
|
||||||
</div>
|
{{! group parameter is used by plugins }}
|
||||||
{{/if}}
|
</td>
|
||||||
</div>
|
</tr>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</:body>
|
</tbody>
|
||||||
|
</table>
|
||||||
</ResponsiveTable>
|
|
||||||
|
|
||||||
</LoadMore>
|
</LoadMore>
|
||||||
|
|
||||||
<ConditionalLoadingSpinner @condition={{this.loading}} />
|
<ConditionalLoadingSpinner @condition={{this.loading}} />
|
||||||
|
|
|
@ -9,14 +9,10 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if this.hasRequesters}}
|
{{#if this.hasRequesters}}
|
||||||
<LoadMore
|
<LoadMore @selector=".group-members tr" @action={{action "loadMore"}}>
|
||||||
@selector=".group-members .directory-table__row"
|
<table class="group-members">
|
||||||
@action={{action "loadMore"}}
|
<thead>
|
||||||
>
|
|
||||||
<ResponsiveTable @className="group-members group-members__requests">
|
|
||||||
<:header>
|
|
||||||
<TableHeaderToggle
|
<TableHeaderToggle
|
||||||
@class="username"
|
|
||||||
@order={{this.order}}
|
@order={{this.order}}
|
||||||
@asc={{this.asc}}
|
@asc={{this.asc}}
|
||||||
@field="username_lower"
|
@field="username_lower"
|
||||||
|
@ -30,34 +26,22 @@
|
||||||
@labelKey="groups.member_requested"
|
@labelKey="groups.member_requested"
|
||||||
@automatic={{true}}
|
@automatic={{true}}
|
||||||
/>
|
/>
|
||||||
<div class="directory-table__column-header">{{i18n
|
<th>{{i18n "groups.requests.reason"}}</th>
|
||||||
"groups.requests.reason"
|
<th></th>
|
||||||
}}</div>
|
<th></th>
|
||||||
<div class="directory-table__column-header"></div>
|
</thead>
|
||||||
</:header>
|
|
||||||
<:body>
|
<tbody>
|
||||||
{{#each this.model.requesters as |m|}}
|
{{#each this.model.requesters as |m|}}
|
||||||
<div class="directory-table__row">
|
<tr>
|
||||||
<div class="directory-table__cell group-member">
|
<td class="group-member">
|
||||||
<UserInfo @user={{m}} @skipName={{this.skipName}} />
|
<UserInfo @user={{m}} @skipName={{this.skipName}} />
|
||||||
</div>
|
</td>
|
||||||
<div class="directory-table__cell">
|
<td>
|
||||||
<span class="directory-table__label">
|
<span class="text">{{bound-date m.requested_at}}</span>
|
||||||
<span>{{i18n "groups.member_requested"}}</span>
|
</td>
|
||||||
</span>
|
<td>{{m.reason}}</td>
|
||||||
<span class="directory-table__value">
|
<td>
|
||||||
<span>{{bound-date m.requested_at}}</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="directory-table__cell">
|
|
||||||
<span class="directory-table__label">
|
|
||||||
<span>{{i18n "groups.requests.reason"}}</span>
|
|
||||||
</span>
|
|
||||||
<span class="directory-table__value">
|
|
||||||
{{m.reason}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="directory-table__cell group-accept-deny-buttons">
|
|
||||||
{{#if m.request_undone}}
|
{{#if m.request_undone}}
|
||||||
{{i18n "groups.requests.undone"}}
|
{{i18n "groups.requests.undone"}}
|
||||||
{{else if m.request_accepted}}
|
{{else if m.request_accepted}}
|
||||||
|
@ -83,14 +67,17 @@
|
||||||
@class="btn-danger"
|
@class="btn-danger"
|
||||||
/>
|
/>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</td>
|
||||||
</div>
|
<td></td>
|
||||||
|
</tr>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</:body>
|
</tbody>
|
||||||
</ResponsiveTable>
|
</table>
|
||||||
</LoadMore>
|
</LoadMore>
|
||||||
|
|
||||||
<ConditionalLoadingSpinner @condition={{this.loading}} />
|
<ConditionalLoadingSpinner @condition={{this.loading}} />
|
||||||
{{else}}
|
{{else}}
|
||||||
<div>{{i18n "groups.empty.requests"}}</div>
|
<div>{{i18n "groups.empty.requests"}}</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
</section>
|
</section>
|
|
@ -0,0 +1,37 @@
|
||||||
|
<UserInfo @user={{this.item.user}} />
|
||||||
|
|
||||||
|
{{#each this.columns as |column|}}
|
||||||
|
{{#if (directory-column-is-user-field column=column)}}
|
||||||
|
{{#if (get this.item.user.user_fields column.user_field_id)}}
|
||||||
|
<div class="user-stat">
|
||||||
|
<span class="value user-field">
|
||||||
|
{{directory-item-user-field-value item=this.item column=column}}
|
||||||
|
</span>
|
||||||
|
<span class="label">
|
||||||
|
{{column.name}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{else}}
|
||||||
|
<div class="user-stat">
|
||||||
|
<span class="value">
|
||||||
|
{{directory-item-value item=this.item column=column}}
|
||||||
|
</span>
|
||||||
|
<span class="label">
|
||||||
|
{{#if column.icon}}
|
||||||
|
{{d-icon column.icon}}
|
||||||
|
{{/if}}
|
||||||
|
{{mobile-directory-item-label item=this.item column=column}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
|
{{#if this.showTimeRead}}
|
||||||
|
<UserStat
|
||||||
|
@value={{this.item.time_read}}
|
||||||
|
@label="directory.time_read"
|
||||||
|
@type="duration"
|
||||||
|
/>
|
||||||
|
{{/if}}
|
|
@ -0,0 +1,80 @@
|
||||||
|
<LoadMore @selector=".directory .user" @action={{action "loadMore"}}>
|
||||||
|
<div class="container">
|
||||||
|
<div class="users-directory directory">
|
||||||
|
<span>
|
||||||
|
<PluginOutlet
|
||||||
|
@name="users-top"
|
||||||
|
@connectorTagName="div"
|
||||||
|
@outletArgs={{hash model=this.model}}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div class="directory-controls">
|
||||||
|
<PeriodChooser
|
||||||
|
@period={{this.period}}
|
||||||
|
@onChange={{action (mut this.period)}}
|
||||||
|
@fullDay={{false}}
|
||||||
|
/>
|
||||||
|
{{#if this.lastUpdatedAt}}
|
||||||
|
<div class="directory-last-updated">
|
||||||
|
{{i18n "directory.last_updated"}}
|
||||||
|
{{this.lastUpdatedAt}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
<div class="inline-form full-width">
|
||||||
|
<Input
|
||||||
|
@value={{readonly this.nameInput}}
|
||||||
|
placeholder={{i18n "directory.filter_name"}}
|
||||||
|
class="filter-name no-blur"
|
||||||
|
{{on
|
||||||
|
"input"
|
||||||
|
(action "onUsernameFilterChanged" value="target.value")
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<ComboBox
|
||||||
|
@class="directory-group-selector"
|
||||||
|
@value={{this.group}}
|
||||||
|
@content={{this.groupOptions}}
|
||||||
|
@onChange={{action this.groupChanged}}
|
||||||
|
@options={{hash none="directory.group.all"}}
|
||||||
|
/>
|
||||||
|
{{#if this.currentUser.staff}}
|
||||||
|
<DButton
|
||||||
|
@icon="wrench"
|
||||||
|
@action={{action "showEditColumnsModal"}}
|
||||||
|
@class="btn-default open-edit-columns-btn"
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
<PluginOutlet
|
||||||
|
@name="users-directory-controls"
|
||||||
|
@outletArgs={{hash model=this.model}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ConditionalLoadingSpinner @condition={{this.isLoading}}>
|
||||||
|
{{#if this.model.length}}
|
||||||
|
<div class="total-rows">{{i18n
|
||||||
|
"directory.total_rows"
|
||||||
|
count=this.model.totalRows
|
||||||
|
}}</div>
|
||||||
|
{{#each this.model as |item|}}
|
||||||
|
<DirectoryItem
|
||||||
|
@tagName="div"
|
||||||
|
@class="user"
|
||||||
|
@item={{item}}
|
||||||
|
@columns={{this.columns}}
|
||||||
|
@showTimeRead={{this.showTimeRead}}
|
||||||
|
/>
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
|
<ConditionalLoadingSpinner @condition={{this.model.loadingMore}} />
|
||||||
|
{{else}}
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
<p>{{i18n "directory.no_results"}}</p>
|
||||||
|
{{/if}}
|
||||||
|
</ConditionalLoadingSpinner>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</LoadMore>
|
|
@ -68,8 +68,7 @@ acceptance("Admin - Users List", function (needs) {
|
||||||
await click(".hide-emails");
|
await click(".hide-emails");
|
||||||
|
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
query(".users-list .user:nth-child(1) .email .directory-table__value")
|
query(".users-list .user:nth-child(1) .email").innerText,
|
||||||
.innerText,
|
|
||||||
"",
|
"",
|
||||||
"hides the emails"
|
"hides the emails"
|
||||||
);
|
);
|
||||||
|
|
|
@ -20,7 +20,7 @@ acceptance("Group Members - Anonymous", function () {
|
||||||
1,
|
1,
|
||||||
"it displays the group's avatar flair"
|
"it displays the group's avatar flair"
|
||||||
);
|
);
|
||||||
assert.ok(exists(".group-members .group-member"), "it lists group members");
|
assert.ok(exists(".group-members tr"), "it lists group members");
|
||||||
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
!exists(".group-member-dropdown"),
|
!exists(".group-member-dropdown"),
|
||||||
|
@ -137,7 +137,7 @@ acceptance("Group Members", function (needs) {
|
||||||
);
|
);
|
||||||
|
|
||||||
await click("button.bulk-select");
|
await click("button.bulk-select");
|
||||||
await click(".bulk-select-all");
|
await click(".bulk-select-buttons button:nth-child(1)");
|
||||||
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
exists(".bulk-select-buttons-wrap details"),
|
exists(".bulk-select-buttons-wrap details"),
|
||||||
|
|
|
@ -12,7 +12,7 @@ acceptance("Managing Group Category Notification Defaults", function () {
|
||||||
await visit("/g/discourse/manage/categories");
|
await visit("/g/discourse/manage/categories");
|
||||||
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
exists(".group-members .group-member"),
|
exists(".group-members tr"),
|
||||||
"it should redirect to members page for an anonymous user"
|
"it should redirect to members page for an anonymous user"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -12,7 +12,7 @@ acceptance("Managing Group Profile", function () {
|
||||||
await visit("/g/discourse/manage/profile");
|
await visit("/g/discourse/manage/profile");
|
||||||
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
exists(".group-members .group-member"),
|
exists(".group-members tr"),
|
||||||
"it should redirect to members page for an anonymous user"
|
"it should redirect to members page for an anonymous user"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -12,7 +12,7 @@ acceptance("Managing Group Tag Notification Defaults", function () {
|
||||||
await visit("/g/discourse/manage/tags");
|
await visit("/g/discourse/manage/tags");
|
||||||
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
exists(".group-members .group-member"),
|
exists(".group-members tr"),
|
||||||
"it should redirect to members page for an anonymous user"
|
"it should redirect to members page for an anonymous user"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -89,49 +89,37 @@ acceptance("Group Requests", function (needs) {
|
||||||
test("Group Requests", async function (assert) {
|
test("Group Requests", async function (assert) {
|
||||||
await visit("/g/Macdonald/requests");
|
await visit("/g/Macdonald/requests");
|
||||||
|
|
||||||
assert.strictEqual(count(".group-members .group-member"), 2);
|
assert.strictEqual(count(".group-members tr"), 2);
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
query(".group-members .directory-table__row:first-child .user-detail")
|
query(".group-members tr:first-child td:nth-child(1)")
|
||||||
.innerText.trim()
|
.innerText.trim()
|
||||||
.replace(/\s+/g, " "),
|
.replace(/\s+/g, " "),
|
||||||
"eviltrout Robin Ward"
|
"eviltrout Robin Ward"
|
||||||
);
|
);
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
query(
|
query(".group-members tr:first-child td:nth-child(3)").innerText.trim(),
|
||||||
".group-members .directory-table__row:first-child .directory-table__cell:nth-child(3)"
|
|
||||||
).innerText.trim(),
|
|
||||||
"Please accept my membership request."
|
"Please accept my membership request."
|
||||||
);
|
);
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
query(
|
query(".group-members tr:first-child .btn-primary").innerText.trim(),
|
||||||
".group-members .directory-table__row:first-child .btn-primary"
|
|
||||||
).innerText.trim(),
|
|
||||||
"Accept"
|
"Accept"
|
||||||
);
|
);
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
query(
|
query(".group-members tr:first-child .btn-danger").innerText.trim(),
|
||||||
".group-members .directory-table__row:first-child .btn-danger"
|
|
||||||
).innerText.trim(),
|
|
||||||
"Deny"
|
"Deny"
|
||||||
);
|
);
|
||||||
|
|
||||||
await click(
|
await click(".group-members tr:first-child .btn-primary");
|
||||||
".group-members .directory-table__row:first-child .btn-primary"
|
|
||||||
);
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
query(
|
query(".group-members tr:first-child td:nth-child(4)")
|
||||||
".group-members .directory-table__row:first-child .directory-table__cell:nth-child(4)"
|
|
||||||
)
|
|
||||||
.innerText.trim()
|
.innerText.trim()
|
||||||
.startsWith("accepted")
|
.startsWith("accepted")
|
||||||
);
|
);
|
||||||
assert.deepEqual(requests, [["19", "true"]]);
|
assert.deepEqual(requests, [["19", "true"]]);
|
||||||
|
|
||||||
await click(".group-members .directory-table__row:last-child .btn-danger");
|
await click(".group-members tr:last-child .btn-danger");
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
query(
|
query(".group-members tr:last-child td:nth-child(4)").innerText.trim(),
|
||||||
".group-members .directory-table__row:last-child .directory-table__cell:nth-child(4)"
|
|
||||||
).innerText.trim(),
|
|
||||||
"denied"
|
"denied"
|
||||||
);
|
);
|
||||||
assert.deepEqual(requests, [
|
assert.deepEqual(requests, [
|
||||||
|
|
|
@ -7,9 +7,6 @@ acceptance("User Directory - Mobile", function (needs) {
|
||||||
|
|
||||||
test("Visit Page", async function (assert) {
|
test("Visit Page", async function (assert) {
|
||||||
await visit("/u");
|
await visit("/u");
|
||||||
assert.ok(
|
assert.ok(exists(".directory .user"), "has a list of users");
|
||||||
exists(".directory .directory-table__row"),
|
|
||||||
"has a list of users"
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,10 +14,7 @@ acceptance("User Directory", function () {
|
||||||
document.body.classList.contains("users-page"),
|
document.body.classList.contains("users-page"),
|
||||||
"has the body class"
|
"has the body class"
|
||||||
);
|
);
|
||||||
assert.ok(
|
assert.ok(exists(".directory table tr"), "has a list of users");
|
||||||
exists(".directory .directory-table .directory-table__row"),
|
|
||||||
"has a list of users"
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Visit All Time", async function (assert) {
|
test("Visit All Time", async function (assert) {
|
||||||
|
@ -31,10 +28,7 @@ acceptance("User Directory", function () {
|
||||||
document.body.classList.contains("users-page"),
|
document.body.classList.contains("users-page"),
|
||||||
"has the body class"
|
"has the body class"
|
||||||
);
|
);
|
||||||
assert.ok(
|
assert.ok(exists(".directory table tr"), "has a list of users");
|
||||||
exists(".directory .directory-table .directory-table__row"),
|
|
||||||
"has a list of users"
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Visit With Group Filter", async function (assert) {
|
test("Visit With Group Filter", async function (assert) {
|
||||||
|
@ -43,27 +37,27 @@ acceptance("User Directory", function () {
|
||||||
document.body.classList.contains("users-page"),
|
document.body.classList.contains("users-page"),
|
||||||
"has the body class"
|
"has the body class"
|
||||||
);
|
);
|
||||||
assert.ok(
|
assert.ok(exists(".directory table tr"), "has a list of users");
|
||||||
exists(".directory .directory-table .directory-table__row"),
|
|
||||||
"has a list of users"
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Custom user fields are present", async function (assert) {
|
test("Custom user fields are present", async function (assert) {
|
||||||
await visit("/u");
|
await visit("/u");
|
||||||
|
|
||||||
const firstRowUserField = query(
|
const firstRow = query(".users-directory table tr");
|
||||||
".directory .directory-table__body .directory-table__row:first-child .directory-table__value--user-field"
|
const columnData = firstRow.querySelectorAll("td");
|
||||||
);
|
const favoriteColorTd = columnData[columnData.length - 1];
|
||||||
|
|
||||||
assert.strictEqual(firstRowUserField.textContent, "Blue");
|
assert.strictEqual(
|
||||||
|
favoriteColorTd.querySelector("span").textContent,
|
||||||
|
"Blue"
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Can sort table via keyboard", async function (assert) {
|
test("Can sort table via keyboard", async function (assert) {
|
||||||
await visit("/u");
|
await visit("/u");
|
||||||
|
|
||||||
const secondHeading =
|
const secondHeading =
|
||||||
".users-directory .directory-table__header div:nth-child(2) .header-contents";
|
".users-directory table th:nth-child(2) .header-contents";
|
||||||
|
|
||||||
await triggerKeyEvent(secondHeading, "keypress", "Enter");
|
await triggerKeyEvent(secondHeading, "keypress", "Enter");
|
||||||
|
|
||||||
|
|
|
@ -802,15 +802,19 @@ section.details {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.directory-table {
|
tr.not-activated {
|
||||||
.not-activated {
|
td,
|
||||||
.directory-table__cell {
|
td a,
|
||||||
&,
|
td a:visited {
|
||||||
a,
|
color: #bbb;
|
||||||
a:visited {
|
}
|
||||||
color: #bbb;
|
}
|
||||||
}
|
|
||||||
}
|
.details.not-activated {
|
||||||
|
.username .value,
|
||||||
|
.email .value a,
|
||||||
|
.email .value a:visited {
|
||||||
|
color: #bbb;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -99,55 +99,49 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-users-list {
|
.admin-users-list {
|
||||||
.directory-table__cell {
|
td.username {
|
||||||
&.username {
|
@include ellipsis;
|
||||||
justify-content: start;
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 970px) and (min-width: 768px) {
|
||||||
|
td.username {
|
||||||
|
max-width: 23vw; // Prevents horizontal scroll down to 768px
|
||||||
}
|
}
|
||||||
&.email {
|
td.email {
|
||||||
justify-content: start;
|
max-width: 28vw; // Prevents horizontal scroll down to 768px
|
||||||
span {
|
overflow-wrap: break-word;
|
||||||
display: flex;
|
|
||||||
min-width: 17em;
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@media screen and (max-width: 767px) {
|
||||||
.directory-table {
|
tr {
|
||||||
margin-top: 1em;
|
td.username {
|
||||||
&__column-header--username,
|
grid-column-start: 1;
|
||||||
&__column-header--email {
|
grid-column-end: -2;
|
||||||
.header-contents {
|
font-weight: bold;
|
||||||
text-align: left;
|
|
||||||
}
|
}
|
||||||
}
|
td.user-status {
|
||||||
|
text-align: right;
|
||||||
&__cell.username {
|
grid-row: 1;
|
||||||
align-items: center;
|
grid-column-end: -1;
|
||||||
}
|
.d-icon {
|
||||||
|
margin-left: 0.25em;
|
||||||
&__cell.email {
|
}
|
||||||
@include breakpoint("tablet") {
|
}
|
||||||
|
td.email {
|
||||||
grid-column-start: 1;
|
grid-column-start: 1;
|
||||||
grid-column-end: -1;
|
grid-column-end: -1;
|
||||||
span {
|
word-wrap: break-word;
|
||||||
max-width: 100%;
|
overflow-wrap: break-word;
|
||||||
|
overflow: hidden;
|
||||||
|
min-width: 0;
|
||||||
|
margin: 0.5em 0 0 0;
|
||||||
|
|
||||||
|
&:empty {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.directory-table__cell {
|
|
||||||
padding: 0.5em 0.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-status span {
|
|
||||||
gap: 0.15em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar {
|
|
||||||
margin-right: 0.25em;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// mobile styles
|
// mobile styles
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
.directory-table-top-scroll {
|
|
||||||
width: 100%;
|
|
||||||
overflow-x: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.directory {
|
.directory {
|
||||||
margin-bottom: 100px;
|
margin-bottom: 100px;
|
||||||
|
|
||||||
|
.directory-table-container {
|
||||||
|
width: 100%;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.directory-table-top-scroll {
|
||||||
|
width: 100%;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
&.users-directory {
|
&.users-directory {
|
||||||
.directory-group-selector {
|
.directory-group-selector {
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
|
@ -34,6 +39,58 @@
|
||||||
color: var(--primary-medium);
|
color: var(--primary-medium);
|
||||||
font-size: var(--font-down-1);
|
font-size: var(--font-down-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
padding: 0.5em;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid var(--primary-low);
|
||||||
|
@media screen and (max-width: $small-width) {
|
||||||
|
padding: 0.5em 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.number,
|
||||||
|
.time-read {
|
||||||
|
font-size: var(--font-up-3);
|
||||||
|
color: var(--primary-medium);
|
||||||
|
@media screen and (max-width: $small-width) {
|
||||||
|
font-size: var(--font-up-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.time-read {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.user-field-value {
|
||||||
|
font-size: var(--font-up-1);
|
||||||
|
color: var(--primary-medium);
|
||||||
|
@media screen and (max-width: $small-width) {
|
||||||
|
font-size: var(--font-0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
th.sortable {
|
||||||
|
width: 13%;
|
||||||
|
.d-icon-heart {
|
||||||
|
color: var(--love);
|
||||||
|
margin: 0 0.25em 0 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.me {
|
||||||
|
background-color: var(--highlight-bg);
|
||||||
|
.username a,
|
||||||
|
.name a,
|
||||||
|
.title,
|
||||||
|
.number,
|
||||||
|
.time-read {
|
||||||
|
color: var(--primary-medium);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.edit-user-directory-columns-modal {
|
.edit-user-directory-columns-modal {
|
||||||
|
@ -82,221 +139,3 @@
|
||||||
.edit-user-directory-columns-modal .modal-inner-container {
|
.edit-user-directory-columns-modal .modal-inner-container {
|
||||||
min-width: 450px;
|
min-width: 450px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@container (min-width: 47em) {
|
|
||||||
.users-directory {
|
|
||||||
.directory-table {
|
|
||||||
&__value {
|
|
||||||
white-space: nowrap;
|
|
||||||
font-size: var(--font-up-2);
|
|
||||||
&,
|
|
||||||
&--user-field {
|
|
||||||
color: var(--primary-medium);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.directory-table {
|
|
||||||
display: grid;
|
|
||||||
gap: 0;
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 1em;
|
|
||||||
overflow-x: auto;
|
|
||||||
|
|
||||||
.me {
|
|
||||||
.directory-table__cell {
|
|
||||||
&,
|
|
||||||
&--user-field {
|
|
||||||
background-color: var(--highlight-low-or-medium);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__header,
|
|
||||||
&__body,
|
|
||||||
&__row {
|
|
||||||
display: contents; // we'll be able to remove this with subgrid support
|
|
||||||
}
|
|
||||||
|
|
||||||
&__column-header,
|
|
||||||
&__cell,
|
|
||||||
&__cell--user-field {
|
|
||||||
display: flex;
|
|
||||||
border-bottom: 1px solid var(--primary-low);
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__column-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
white-space: nowrap;
|
|
||||||
color: var(--primary-medium);
|
|
||||||
padding: 0.5em;
|
|
||||||
.d-icon {
|
|
||||||
margin-right: 0.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
.header-contents {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__cell {
|
|
||||||
&,
|
|
||||||
&--user-field {
|
|
||||||
padding: 0.75em 0.5em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__value {
|
|
||||||
white-space: nowrap;
|
|
||||||
&--user-field {
|
|
||||||
max-width: 30em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__label {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d-icon-heart {
|
|
||||||
font-size: var(--font-down-1);
|
|
||||||
color: var(--love);
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-detail {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-width: 0; // allow content to shrink and hide overflow
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-info {
|
|
||||||
display: flex;
|
|
||||||
min-width: 0;
|
|
||||||
margin: 0;
|
|
||||||
width: 100%;
|
|
||||||
.user-image {
|
|
||||||
padding-right: 0.5em;
|
|
||||||
margin-right: 0.5em;
|
|
||||||
}
|
|
||||||
.user-detail {
|
|
||||||
padding: 0;
|
|
||||||
width: 100%;
|
|
||||||
@media screen and (max-width: 600px) {
|
|
||||||
// overrides existing media query
|
|
||||||
font-size: var(--font-0);
|
|
||||||
}
|
|
||||||
@include breakpoint("mobile-medium") {
|
|
||||||
font-size: var(--font-down-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.title {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-contents {
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// using a container query to switch to a flex-based layout
|
|
||||||
// browsers without support for container queries
|
|
||||||
// fallback to big horizontal scrolling table
|
|
||||||
|
|
||||||
@container (max-width: 47em) {
|
|
||||||
.directory-table {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.me {
|
|
||||||
background-color: var(--highlight-low-or-medium);
|
|
||||||
}
|
|
||||||
|
|
||||||
&__label {
|
|
||||||
display: inline-flex;
|
|
||||||
color: var(--primary-medium);
|
|
||||||
padding-right: 0.5em;
|
|
||||||
align-items: baseline;
|
|
||||||
align-self: start;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
span {
|
|
||||||
// caution: display flex here can interfere with overflow hiding
|
|
||||||
flex: 0 1 auto; // can shrink if needed
|
|
||||||
margin-right: 0.25em;
|
|
||||||
@include ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
// flexible divider between the label and value
|
|
||||||
&:after {
|
|
||||||
flex: 1 1 0; // can grow or shrink, but should be 0 width if needed
|
|
||||||
color: var(--primary-300);
|
|
||||||
min-width: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
// this needs to be long to account for all possible widths
|
|
||||||
content: "...................................................................................................................................";
|
|
||||||
}
|
|
||||||
|
|
||||||
.d-icon {
|
|
||||||
font-size: 0.8em;
|
|
||||||
vertical-align: baseline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__value {
|
|
||||||
font-size: var(--font-0);
|
|
||||||
color: var(--primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
&__row {
|
|
||||||
&:first-child {
|
|
||||||
border-top: 1px solid var(--primary-low);
|
|
||||||
}
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fill, minmax(11em, 1fr));
|
|
||||||
border-bottom: 1px solid var(--primary-low);
|
|
||||||
padding: 0.85em 0.75em 1em;
|
|
||||||
gap: 0 15%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__header {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__cell {
|
|
||||||
&,
|
|
||||||
&--user-field {
|
|
||||||
padding: 0.25em;
|
|
||||||
border: none;
|
|
||||||
&:first-child {
|
|
||||||
width: 100%;
|
|
||||||
padding: 0.5em 0.25em 1em;
|
|
||||||
justify-content: start;
|
|
||||||
// force full width of the cell
|
|
||||||
grid-column-start: 1;
|
|
||||||
grid-column-end: -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&--user-field {
|
|
||||||
order: 2;
|
|
||||||
// force full width of the cell
|
|
||||||
// because we don't know how much content there is
|
|
||||||
grid-column-start: 1;
|
|
||||||
grid-column-end: -1;
|
|
||||||
.directory-table__label {
|
|
||||||
margin-right: 0.25em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -596,8 +596,6 @@ table {
|
||||||
|
|
||||||
#main-outlet {
|
#main-outlet {
|
||||||
grid-area: content;
|
grid-area: content;
|
||||||
container-type: inline-size;
|
|
||||||
container-name: main-outlet;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,27 +25,15 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
gap: 0.5em 0;
|
|
||||||
|
|
||||||
.bulk-select + input {
|
input + .group-members-manage {
|
||||||
margin-left: 0.5em;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
.group-username-filter {
|
||||||
margin: 0 auto 0 0;
|
margin: 0 0 5px 0;
|
||||||
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bulk-select-buttons-wrap {
|
|
||||||
margin-right: 0.5em;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 0.5em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.group-members-manage {
|
|
||||||
display: flex;
|
|
||||||
gap: 0.5em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.group-info {
|
.group-info {
|
||||||
|
@ -130,45 +118,58 @@ table.group-manage-logs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.group-members {
|
table.group-members {
|
||||||
grid-template-columns: 3fr repeat(3, minmax(min-content, 1fr));
|
width: 100%;
|
||||||
|
|
||||||
&--can-manage {
|
th {
|
||||||
grid-template-columns: 3fr repeat(4, minmax(min-content, 1fr)) 3em;
|
text-align: center;
|
||||||
@container (max-width: 47em) {
|
|
||||||
// positioning the member settings button within the same cell
|
&.bulk-select {
|
||||||
// and avoiding overlap with padding-right on user-info
|
height: 30px;
|
||||||
.group-member,
|
width: 30px;
|
||||||
.member-settings {
|
}
|
||||||
grid-row-start: 1;
|
|
||||||
grid-column-start: 1;
|
&.bulk-select-buttons {
|
||||||
grid-column-end: -1;
|
text-align: left;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 1%;
|
||||||
|
|
||||||
|
.bulk-select-buttons-wrap {
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
.member-settings {
|
|
||||||
margin-left: auto;
|
.btn {
|
||||||
}
|
margin-right: 0.25em;
|
||||||
.user-info {
|
|
||||||
padding-right: 3.5em;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.username {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.group-members__requests {
|
td {
|
||||||
grid-template-columns: 3fr repeat(3, minmax(min-content, 1fr));
|
color: var(--primary-medium);
|
||||||
|
padding: 0.8em 0;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&.group-member {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.directory-table__value {
|
.user-info {
|
||||||
font-size: var(--font-0);
|
display: block;
|
||||||
color: var(--primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.group-accept-deny-buttons {
|
.avatar-flair {
|
||||||
gap: 0.5em;
|
color: var(--primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
@container (max-width: 47em) {
|
.user-status-message {
|
||||||
.directory-table__cell.group-owner {
|
img.emoji {
|
||||||
order: 2;
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,35 @@
|
||||||
color: var(--primary-medium);
|
color: var(--primary-medium);
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.user {
|
||||||
|
border-top: 1px solid var(--primary-low);
|
||||||
|
padding: 1em;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
.user-info {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-stat {
|
||||||
|
flex: 1 1 50%;
|
||||||
|
.value {
|
||||||
|
font-weight: bold;
|
||||||
|
&.user-field {
|
||||||
|
font-size: var(--font-down-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.label {
|
||||||
|
margin-left: 0.2em;
|
||||||
|
color: var(--primary-medium);
|
||||||
|
}
|
||||||
|
.d-icon-heart {
|
||||||
|
color: var(--love);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.edit-user-directory-columns-modal .modal-inner-container {
|
.edit-user-directory-columns-modal .modal-inner-container {
|
||||||
|
|
|
@ -923,7 +923,6 @@ en:
|
||||||
make_all_primary_description: "Make this the primary group for all selected users"
|
make_all_primary_description: "Make this the primary group for all selected users"
|
||||||
remove_all_primary: "Remove as Primary"
|
remove_all_primary: "Remove as Primary"
|
||||||
remove_all_primary_description: "Remove this group as primary"
|
remove_all_primary_description: "Remove this group as primary"
|
||||||
status: "Status"
|
|
||||||
owner: "Owner"
|
owner: "Owner"
|
||||||
primary: "Primary"
|
primary: "Primary"
|
||||||
forbidden: "You're not allowed to view the members."
|
forbidden: "You're not allowed to view the members."
|
||||||
|
@ -5630,7 +5629,6 @@ en:
|
||||||
not_found: "Sorry, that username doesn't exist in our system."
|
not_found: "Sorry, that username doesn't exist in our system."
|
||||||
id_not_found: "Sorry, that user id doesn't exist in our system."
|
id_not_found: "Sorry, that user id doesn't exist in our system."
|
||||||
active: "Activated"
|
active: "Activated"
|
||||||
status: "Status"
|
|
||||||
show_emails: "Show Emails"
|
show_emails: "Show Emails"
|
||||||
hide_emails: "Hide Emails"
|
hide_emails: "Hide Emails"
|
||||||
nav:
|
nav:
|
||||||
|
|
Loading…
Reference in New Issue