AdminUX Overhaul Small Patches (#2468)

This commit is contained in:
Charlie 2020-12-07 12:14:22 -08:00 committed by GitHub
parent d6a20567af
commit 6d07820952
11 changed files with 231 additions and 144 deletions

View File

@ -21,6 +21,34 @@ export default class AdminNav extends Component {
);
}
oncreate(vnode) {
super.oncreate(vnode);
this.scrollToActive();
}
onupdate() {
this.scrollToActive();
}
scrollToActive() {
const children = $('.Dropdown-menu').children('.active');
const nav = $('#admin-navigation');
const time = app.previous.type ? 250 : 0;
if (
children.length > 0 &&
(children[0].offsetTop > nav.scrollTop() + nav.outerHeight() || children[0].offsetTop + children[0].offsetHeight < nav.scrollTop())
) {
nav.animate(
{
scrollTop: children[0].offsetTop - nav.height() / 2,
},
time
);
}
}
/**
* Build an item list of main links to show in the admin navigation.
*
@ -29,6 +57,8 @@ export default class AdminNav extends Component {
items() {
const items = new ItemList();
items.add('category-core', <h4 className="ExtensionListTitle">{app.translator.trans('core.admin.nav.categories.core')}</h4>);
items.add(
'dashboard',
<LinkButton href={app.route('dashboard')} icon="far fa-chart-bar" title={app.translator.trans('core.admin.nav.dashboard_title')}>
@ -88,7 +118,7 @@ export default class AdminNav extends Component {
Object.keys(categorizedExtensions).map((category) => {
if (!this.query()) {
items.add(
category,
`category-${category}`,
<h4 className="ExtensionListTitle">{app.translator.trans(`core.admin.nav.categories.${category}`)}</h4>,
categories[category]
);
@ -100,7 +130,7 @@ export default class AdminNav extends Component {
if (!query || title.toUpperCase().includes(query) || extension.description.toUpperCase().includes(query)) {
items.add(
extension.id,
`extension-${extension.id}`,
<ExtensionLinkButton
href={app.route('extension', { id: extension.id })}
extensionId={extension.id}

View File

@ -12,7 +12,6 @@ import Stream from '../../common/utils/Stream';
import LoadingModal from './LoadingModal';
import ExtensionPermissionGrid from './ExtensionPermissionGrid';
import saveSettings from '../utils/saveSettings';
import ExtensionData from '../utils/ExtensionData';
import isExtensionEnabled from '../utils/isExtensionEnabled';
export default class ExtensionPage extends Page {
@ -30,6 +29,7 @@ export default class ExtensionPage extends Page {
support: 'fas fa-life-ring',
website: 'fas fa-link',
donate: 'fas fa-donate',
source: 'fas fa-code',
};
// Backwards compatibility layer will be removed in
@ -49,7 +49,7 @@ export default class ExtensionPage extends Page {
{this.header()}
{!this.isEnabled() ? (
<div className="container">
<h2 className="ExtensionPage-subHeader">{app.translator.trans('core.admin.extension.enable_to_see')}</h2>
<h3 className="ExtensionPage-subHeader">{app.translator.trans('core.admin.extension.enable_to_see')}</h3>
</div>
) : (
<div className="ExtensionPage-body">{this.sections().toArray()}</div>
@ -105,7 +105,7 @@ export default class ExtensionPage extends Page {
{app.extensionData.extensionHasPermissions(this.extension.id) ? (
ExtensionPermissionGrid.component({ extensionId: this.extension.id })
) : (
<h2 className="ExtensionPage-subHeader">{app.translator.trans('core.admin.extension.no_permissions')}</h2>
<h3 className="ExtensionPage-subHeader">{app.translator.trans('core.admin.extension.no_permissions')}</h3>
)}
</div>
</div>,
@ -130,7 +130,7 @@ export default class ExtensionPage extends Page {
<div className="Form-group">{this.submitButton()}</div>
</div>
) : (
<h2 className="ExtensionPage-subHeader">{app.translator.trans('core.admin.extension.no_settings')}</h2>
<h3 className="ExtensionPage-subHeader">{app.translator.trans('core.admin.extension.no_settings')}</h3>
)}
</div>
</div>
@ -170,17 +170,15 @@ export default class ExtensionPage extends Page {
infoItems() {
const items = new ItemList();
if (this.extension.authors) {
const links = this.extension.links;
if (links.authors.length) {
let authors = [];
Object.keys(this.extension.authors).map((author, i) => {
const link = this.extension.authors[author].homepage
? this.extension.authors[author].homepage
: 'mailto:' + this.extension.authors[author].email;
links.authors.map((author) => {
authors.push(
<Link href={link} external={true} target="_blank">
{this.extension.authors[author].name}
<Link href={author.link} external={true} target="_blank">
{author.name}
</Link>
);
});
@ -188,35 +186,17 @@ export default class ExtensionPage extends Page {
items.add('authors', [icon('fas fa-user'), <span>{punctuateSeries(authors)}</span>]);
}
const infoData = {};
if (this.extension.source || this.extension.support) {
infoData.source = {
icon: 'fas fa-code',
href: this.extension.source ? this.extension.source.url : this.extension.support.source,
};
}
Object.keys(this.infoFields).map((field) => {
const info = this.extension.extra['flarum-extension'].info;
if (info && info[field]) {
infoData[field] = {
icon: this.infoFields[field],
href: info[field],
};
if (links[field]) {
items.add(
field,
<LinkButton href={links[field]} icon={this.infoFields[field]} external={true} target="_blank">
{app.translator.trans(`core.admin.extension.info_links.${field}`)}
</LinkButton>
);
}
});
Object.entries(infoData).map(([field, value]) => {
items.add(
field,
<LinkButton href={value.href} icon={value.icon} external={true} target="_blank">
{app.translator.trans(`core.admin.extension.info_links.${field}`)}
</LinkButton>
);
});
return items;
}
@ -233,6 +213,9 @@ export default class ExtensionPage extends Page {
* Depending on the type of input, you can set the type to 'bool', 'select', or
* any standard <input> type.
*
* Alternatively, you can pass a callback that will be executed in ExtensionPage's
* context to include custom JSX elements.
*
* @example
*
* {
@ -258,6 +241,10 @@ export default class ExtensionPage extends Page {
* @returns {JSX.Element}
*/
buildSettingComponent(entry) {
if (typeof entry === 'function') {
return entry.call(this);
}
const setting = entry.setting;
const value = this.setting([setting])();
if (['bool', 'checkbox', 'switch', 'boolean'].includes(entry.type)) {

View File

@ -15,33 +15,31 @@ export default class ExtensionsWidget extends DashboardWidget {
return (
<div className="ExtensionsWidget-list">
<div className="container">
{Object.keys(categories).map((category) => {
if (categorizedExtensions[category]) {
return (
<div className="ExtensionList-Category">
<h4 className="ExtensionList-Label">{app.translator.trans(`core.admin.nav.categories.${category}`)}</h4>
<ul className="ExtensionList">
{categorizedExtensions[category].map((extension) => {
return (
<li className={'ExtensionListItem ' + (!isExtensionEnabled(extension.id) ? 'disabled' : '')}>
<Link href={app.route('extension', { id: extension.id })}>
<div className="ExtensionListItem-content">
<span className="ExtensionListItem-icon ExtensionIcon" style={extension.icon}>
{extension.icon ? icon(extension.icon.name) : ''}
</span>
<span className="ExtensionListItem-title">{extension.extra['flarum-extension'].title}</span>
</div>
</Link>
</li>
);
})}
</ul>
</div>
);
}
})}
</div>
{Object.keys(categories).map((category) => {
if (categorizedExtensions[category]) {
return (
<div className="ExtensionList-Category">
<h4 className="ExtensionList-Label">{app.translator.trans(`core.admin.nav.categories.${category}`)}</h4>
<ul className="ExtensionList">
{categorizedExtensions[category].map((extension) => {
return (
<li className={'ExtensionListItem ' + (!isExtensionEnabled(extension.id) ? 'disabled' : '')}>
<Link href={app.route('extension', { id: extension.id })}>
<div className="ExtensionListItem-content">
<span className="ExtensionListItem-icon ExtensionIcon" style={extension.icon}>
{extension.icon ? icon(extension.icon.name) : ''}
</span>
<span className="ExtensionListItem-title">{extension.extra['flarum-extension'].title}</span>
</div>
</Link>
</li>
);
})}
</ul>
</div>
);
}
})}
</div>
);
}

View File

@ -26,6 +26,8 @@ export default class ExtensionData {
/**
* This function registers your settings with Flarum
*
* It takes either a settings object or a callback.
*
* @example
*
* .registerSetting({

View File

@ -11,6 +11,7 @@
.AdminHeader-description {
margin: 0;
color: @control-color;
}
.icon {

View File

@ -41,16 +41,13 @@
}
@media @tablet {
.item-search{
display: none;
}
.ExtensionItem, .item-search {
display: none !important;
}
.ExtensionListTitle {
display: none !important;
.AdminNav {
.item-search,
li[class^="item-category"],
li[class^="item-extension"],
.AdminLinkButton-description {
display: none !important;
}
}
}
@ -80,7 +77,7 @@
}
@media @desktop, @desktop-hd {
@media @desktop-up {
.App-nav {
position: absolute;
top: @header-height;
@ -107,36 +104,47 @@
margin-bottom: 20px;
}
.item-category-core {
> .ExtensionListTitle {
margin-top: 10px;
}
}
> li {
> a {
padding: 10px 10px 10px 45px;
display: block;
text-decoration: none;
}
> a,
> a:hover,
&.active > a {
color: @text-color;
}
> a:hover {
background: @control-bg;
}
&.active > a {
background: @primary-color;
background: @control-color;
font-weight: normal;
color: @body-bg;
.Button-label,
.Button-icon {
color: @body-bg;
font-weight: bold;
}
}
.Button-icon {
float: left;
font-size: 13px !important;
margin-left: -25px !important;
margin-top: 4px !important;
}
.Button-label {
padding-left: 5px;
font-size: 14px;
@ -152,7 +160,7 @@
.ExtensionListTitle {
color: @muted-color;
text-transform: uppercase;
margin: 25px 0 15px 15px;
margin: 25px 0 8px 15px;
}
.ExtensionIcon {
@ -180,6 +188,11 @@
}
.AdminLinkButton-description {
white-space: normal;
padding-left: 5px;
}
.ExtensionListItem-Dot {
height: 10px;
width: 10px;

View File

@ -10,17 +10,21 @@
border-radius: @border-radius;
padding: 20px;
margin-bottom: 20px;
.Button {
.Button--color(@control-color, @body-bg)
}
}
.StatusWidget {
color: @muted-color;
> ul {
>ul {
margin: 0;
padding: 0;
list-style-type: none;
> li {
>li {
display: inline-block;
margin-right: 30px;
vertical-align: middle;
@ -31,6 +35,7 @@
overflow: hidden;
text-overflow: ellipsis;
}
&.item-tools {
float: right;
margin-right: 0;

View File

@ -119,6 +119,11 @@
}
}
.ExtensionPage-settings, .ExtensionPage-permissions {
.ExtensionPage-subHeader {
margin: 5px 0px;
}
}
.ExtensionPage-settings {
margin-top: 20px;
@ -132,7 +137,6 @@
.ExtensionPage-subHeader {
color: @muted-color;
font-weight: normal;
text-align: center;
}

View File

@ -4,77 +4,75 @@
}
.ExtensionsWidget-list {
> .container {
padding: 0;
background-color: @body-bg;
padding: 0;
background-color: @body-bg;
.ExtensionList-Category {
background: @control-bg;
padding: 20px 0 20px 20px;
margin-bottom: 20px;
border-radius: @border-radius;
.ExtensionList-Category {
background: @control-bg;
padding: 20px 0 20px 20px;
margin-bottom: 20px;
border-radius: @border-radius;
.ExtensionList-Label {
margin-top: 0;
color: @muted-color;
}
}
.ExtensionGroup {
margin-bottom: 20px;
h3 {
color: @muted-color;
text-transform: uppercase;
font-size: 12px;
margin: 0 0 10px;
}
}
.ExtensionList {
padding: 0;
list-style: none;
display: grid;
grid-gap: 10px;
grid-template-columns: repeat(auto-fit, 90px);
margin-bottom: 0;
> li {
text-align: left;
position: relative;
display: block;
}
}
}
.ExtensionListItem.disabled {
.ExtensionListItem-title {
opacity: 0.5;
.ExtensionList-Label {
margin-top: 0;
color: @muted-color;
}
}
.ExtensionListItem-icon {
opacity: 0.5;
.ExtensionGroup {
margin-bottom: 20px;
h3 {
color: @muted-color;
text-transform: uppercase;
font-size: 12px;
margin: 0 0 10px;
}
}
.ExtensionListItem {
transition: .15s ease-in-out;
.ExtensionList {
padding: 0;
list-style: none;
display: grid;
grid-gap: 10px;
grid-template-columns: repeat(auto-fit, 90px);
margin-bottom: 0;
&:hover {
transform: scale(1.05);
}
.ExtensionListItem-title {
> li {
text-align: left;
position: relative;
display: block;
text-align: center;
margin-top: 5px;
color: @text-color;
}
}
}
a:hover {
text-decoration: none;
}
.ExtensionListItem.disabled {
.ExtensionListItem-title {
opacity: 0.5;
color: @muted-color;
}
.ExtensionListItem-icon {
opacity: 0.5;
}
}
.ExtensionListItem {
transition: .15s ease-in-out;
&:hover {
transform: scale(1.05);
}
.ExtensionListItem-title {
display: block;
text-align: center;
margin-top: 5px;
color: @text-color;
}
a:hover {
text-decoration: none;
}
}

View File

@ -5,7 +5,11 @@
display: block;
margin-left: 30px;
overflow-x: auto;
padding: 8px 0 8px;
padding: 10px 0 8px;
> .container {
padding: 0 10px;
}
}
.Group {
width: 90px;
@ -42,6 +46,7 @@
.PermissionGrid {
white-space: nowrap;
padding-left: 0 12px;
td, th {
padding: 5px;
@ -117,7 +122,7 @@
}
.PermissionGrid-section {
td, th {
padding-top: 20px;
padding-top: 10px;
}
}
.PermissionGrid-child {

View File

@ -346,6 +346,49 @@ class Extension implements Arrayable
return null;
}
/**
* Compile a list of links for this extension.
*/
public function getLinks()
{
$links = [];
if (($sourceUrl = $this->composerJsonAttribute('source.url')) || ($sourceUrl = $this->composerJsonAttribute('support.source'))) {
$links['source'] = $sourceUrl;
}
if (($discussUrl = $this->composerJsonAttribute('support.forum'))) {
$links['discuss'] = $discussUrl;
}
if (($documentationUrl = $this->composerJsonAttribute('support.docs'))) {
$links['documentation'] = $documentationUrl;
}
if (($websiteUrl = $this->composerJsonAttribute('homepage'))) {
$links['website'] = $websiteUrl;
}
if (($supportEmail = $this->composerJsonAttribute('support.email'))) {
$links['support'] = "mailto:$supportEmail";
}
if (($funding = $this->composerJsonAttribute('funding')) && count($funding)) {
$links['donate'] = $funding[0]['url'];
}
$links['authors'] = [];
foreach ((array) $this->composerJsonAttribute('authors') as $author) {
$links['authors'][] = [
'name' => Arr::get($author, 'name'),
'link' => Arr::get($author, 'homepage') ?? (Arr::get($author, 'email') ? 'mailto:'.Arr::get($author, 'email') : ''),
];
}
return array_merge($links, $this->composerJsonAttribute('flarum-extension.links') ?? []);
}
/**
* Tests whether the extension has assets.
*
@ -413,6 +456,7 @@ class Extension implements Arrayable
'hasAssets' => $this->hasAssets(),
'hasMigrations' => $this->hasMigrations(),
'extensionDependencyIds' => $this->getExtensionDependencyIds(),
'links' => $this->getLinks(),
], $this->composerJson);
}
}