feedback (see commit description for details)

* fill blank space when no theme is selected
* animate row's height in themes/components list when selecting, and hide children list
* show warning when you move to a different page and have unsaved changes
* refactor `adminCustomizeThemes.show` controller
* allow collapsing/expanding children lists
* fix a bug when adding components to a theme (changed the way it works slightly)
* a bunch of other minor things
This commit is contained in:
OsamaSayegh 2018-09-06 21:56:00 +03:00 committed by Sam
parent a4f057a589
commit ca28548762
15 changed files with 372 additions and 208 deletions

View File

@ -1,32 +1,136 @@
import { default as computed } from "ember-addons/ember-computed-decorators"; import {
default as computed,
observes
} from "ember-addons/ember-computed-decorators";
const MAX_COMPONENTS = 4; const MAX_COMPONENTS = 4;
export default Ember.Component.extend({ export default Ember.Component.extend({
childrenExpanded: false,
classNames: ["themes-list-item"], classNames: ["themes-list-item"],
classNameBindings: ["theme.active:active"], classNameBindings: ["theme.selected:selected"],
hasComponents: Em.computed.gt("children.length", 0), hasComponents: Em.computed.gt("children.length", 0),
hasMore: Em.computed.gt("moreCount", 0), displayComponents: Em.computed.and("hasComponents", "theme.isActive"),
displayHasMore: Em.computed.gt("theme.childThemes.length", MAX_COMPONENTS),
click(e) {
if (!$(e.target).hasClass("others-count")) {
this.navigateToTheme();
}
},
init() {
this._super(...arguments);
this.scheduleAnimation();
},
@observes("theme.selected")
triggerAnimation() {
this.animate();
},
scheduleAnimation() {
Ember.run.schedule("afterRender", () => {
this.animate(true);
});
},
animate(isInitial) {
const $container = this.$();
const $list = this.$(".components-list");
if ($list.length === 0 || Ember.testing) {
return;
}
const duration = 300;
if (this.get("theme.selected")) {
this.collapseComponentsList($container, $list, duration);
} else if (!isInitial) {
this.expandComponentsList($container, $list, duration);
}
},
@computed( @computed(
"theme.component", "theme.component",
"theme.childThemes.@each.name", "theme.childThemes.@each.name",
"theme.childThemes.length" "theme.childThemes.length",
"childrenExpanded"
) )
children() { children() {
const theme = this.get("theme"); const theme = this.get("theme");
const children = theme.get("childThemes"); let children = theme.get("childThemes");
if (theme.get("component") || !children) { if (theme.get("component") || !children) {
return []; return [];
} }
return children.slice(0, MAX_COMPONENTS).map(t => t.get("name")); children = this.get("childrenExpanded")
? children
: children.slice(0, MAX_COMPONENTS);
return children.map(t => t.get("name"));
}, },
@computed("theme.childThemes.length", "theme.component", "children.length") @computed(
moreCount(childrenCount, component) { "theme.childThemes.length",
if (component || !childrenCount) { "theme.component",
"childrenExpanded",
"children.length"
)
moreCount(childrenCount, component, expanded) {
if (component || !childrenCount || expanded) {
return 0; return 0;
} }
return childrenCount - MAX_COMPONENTS; return childrenCount - MAX_COMPONENTS;
},
expandComponentsList($container, $list, duration) {
$container.css("height", `${$container.height()}px`);
$list.css("display", "");
$container.animate(
{
height: `${$container.height() + $list.outerHeight(true)}px`
},
{
duration,
done: () => {
$list.css("display", "");
$container.css("height", "");
}
}
);
$list.animate(
{
opacity: 1
},
{
duration
}
);
},
collapseComponentsList($container, $list, duration) {
$container.animate(
{
height: `${$container.height() - $list.outerHeight(true)}px`
},
{
duration,
done: () => {
$list.css("display", "none");
$container.css("height", "");
}
}
);
$list.animate(
{
opacity: 0
},
{
duration
}
);
},
actions: {
toggleChildrenExpanded() {
this.toggleProperty("childrenExpanded");
}
} }
}); });

View File

@ -1,7 +1,7 @@
import { THEMES, COMPONENTS } from "admin/models/theme"; import { THEMES, COMPONENTS } from "admin/models/theme";
import { default as computed } from "ember-addons/ember-computed-decorators"; import { default as computed } from "ember-addons/ember-computed-decorators";
const NUM_ENTRIES = 8; const MAX_LIST_HEIGHT = 700;
export default Ember.Component.extend({ export default Ember.Component.extend({
THEMES: THEMES, THEMES: THEMES,
@ -63,15 +63,24 @@ export default Ember.Component.extend({
}, },
didRender() { didRender() {
let height = -1; this._super(...arguments);
this.$(".themes-list-item")
.slice(0, NUM_ENTRIES) // hide scrollbar
.each(function() { const $container = this.$(".themes-list-container");
height += $(this).outerHeight(); const containerNode = $container[0];
}); if (containerNode) {
if (height >= 485 && height <= 800) { const width = containerNode.offsetWidth - containerNode.clientWidth;
this.$(".themes-list-container").css("max-height", `${height}px`); $container.css("width", `calc(100% + ${width}px)`);
} }
let height = -1;
Array.from(this.$(".themes-list-item")).forEach(node => {
const nodeHeight = $(node).outerHeight();
if (height + nodeHeight <= MAX_LIST_HEIGHT) {
height += nodeHeight;
}
});
$container.css("max-height", `${height}px`);
}, },
actions: { actions: {
@ -79,6 +88,11 @@ export default Ember.Component.extend({
if (newTab !== this.get("currentTab")) { if (newTab !== this.get("currentTab")) {
this.set("currentTab", newTab); this.set("currentTab", newTab);
} }
},
navigateToTheme(theme) {
Em.getOwner(this)
.lookup("router:main")
.transitionTo("adminCustomizeThemes.show", theme);
} }
} }
}); });

View File

@ -5,8 +5,10 @@ import {
} from "ember-addons/ember-computed-decorators"; } from "ember-addons/ember-computed-decorators";
export default Ember.Controller.extend({ export default Ember.Controller.extend({
maximized: false,
section: null, section: null,
currentTarget: 0,
maximized: false,
previewUrl: url("model.id", "/admin/themes/%@/preview"),
editRouteName: "adminCustomizeThemes.edit", editRouteName: "adminCustomizeThemes.edit",
@ -86,8 +88,6 @@ export default Ember.Controller.extend({
return this.get("model").hasEdited(target); return this.get("model").hasEdited(target);
}, },
currentTarget: 0,
setTargetName: function(name) { setTargetName: function(name) {
const target = this.get("targets").find(t => t.name === name); const target = this.get("targets").find(t => t.name === name);
this.set("currentTarget", target && target.id); this.set("currentTarget", target && target.id);
@ -152,21 +152,20 @@ export default Ember.Controller.extend({
}); });
}, },
previewUrl: url("model.id", "/admin/themes/%@/preview"), @computed("maximized")
maximizeIcon(maximized) {
return maximized ? "compress" : "expand";
},
maximizeIcon: function() { @computed("model.isSaving")
return this.get("maximized") ? "compress" : "expand"; saveButtonText(isSaving) {
}.property("maximized"), return isSaving ? I18n.t("saving") : I18n.t("admin.customize.save");
},
saveButtonText: function() { @computed("model.changed", "model.isSaving")
return this.get("model.isSaving") saveDisabled(changed, isSaving) {
? I18n.t("saving") return !changed || isSaving;
: I18n.t("admin.customize.save"); },
}.property("model.isSaving"),
saveDisabled: function() {
return !this.get("model.changed") || this.get("model.isSaving");
}.property("model.changed", "model.isSaving"),
actions: { actions: {
save() { save() {

View File

@ -9,106 +9,65 @@ import ThemeSettings from "admin/models/theme-settings";
import { THEMES, COMPONENTS } from "admin/models/theme"; import { THEMES, COMPONENTS } from "admin/models/theme";
const THEME_UPLOAD_VAR = 2; const THEME_UPLOAD_VAR = 2;
const SETTINGS_TYPE_ID = 5;
export default Ember.Controller.extend({ export default Ember.Controller.extend({
editRouteName: "adminCustomizeThemes.edit", downloadUrl: url("model.id", "/admin/themes/%@"),
previewUrl: url("model.id", "/admin/themes/%@/preview"),
@observes("allowChildThemes") addButtonDisabled: Em.computed.empty("selectedChildThemeId"),
setSelectedThemeId() {
const available = this.get("selectableChildThemes");
if (
!this.get("selectedChildThemeId") &&
available &&
available.length > 0
) {
this.set("selectedChildThemeId", available[0].get("id"));
}
},
@computed("model", "allThemes", "model.component") @computed("model", "allThemes", "model.component")
parentThemes(model, allThemes) { parentThemes(model, allThemes) {
if (!model.get("component")) { if (!model.get("component")) {
return null; return null;
} }
let parents = allThemes.filter(theme => const parents = allThemes.filter(theme =>
_.contains(theme.get("childThemes"), model) _.contains(theme.get("childThemes"), model)
); );
return parents.length === 0 ? null : parents; return parents.length === 0 ? null : parents;
}, },
@computed("model.theme_fields.@each") @computed("model.editedFields")
hasEditedFields(fields) { editedFieldsFormatted(fields) {
return fields.any( const descriptions = [];
f => !Em.isBlank(f.value) && f.type_id !== SETTINGS_TYPE_ID
);
},
@computed("model.theme_fields.@each")
editedDescriptions(fields) {
let descriptions = [];
let description = target => {
let current = fields.filter(
field => field.target === target && !Em.isBlank(field.value)
);
if (current.length > 0) {
let text = I18n.t("admin.customize.theme." + target);
let localized = current.map(f =>
I18n.t("admin.customize.theme." + f.name + ".text")
);
return text + ": " + localized.join(" , ");
}
};
["common", "desktop", "mobile"].forEach(target => { ["common", "desktop", "mobile"].forEach(target => {
descriptions.push(description(target)); const fields = this.editedFieldsForTarget(target);
if (fields.length < 1) {
return;
}
let resultString = I18n.t("admin.customize.theme." + target);
const formattedFields = fields
.map(f => I18n.t("admin.customize.theme." + f.name + ".text"))
.join(" , ");
resultString += `: ${formattedFields}`;
descriptions.push(resultString);
}); });
return descriptions.reject(d => Em.isBlank(d)); return descriptions;
}, },
previewUrl: url("model.id", "/admin/themes/%@/preview"),
@computed("colorSchemeId", "model.color_scheme_id") @computed("colorSchemeId", "model.color_scheme_id")
colorSchemeChanged(colorSchemeId, existingId) { colorSchemeChanged(colorSchemeId, existingId) {
colorSchemeId = colorSchemeId === null ? null : parseInt(colorSchemeId); colorSchemeId = colorSchemeId === null ? null : parseInt(colorSchemeId);
return colorSchemeId !== existingId; return colorSchemeId !== existingId;
}, },
@computed( @computed("availableChildThemes", "model.childThemes.@each", "model")
"availableChildThemes", selectableChildThemes(available, childThemes) {
"model.childThemes.@each", if (available) {
"model", const themes = !childThemes
"allowChildThemes" ? available
) : available.filter(theme => childThemes.indexOf(theme) === -1);
selectableChildThemes(available, childThemes, allowChildThemes) { return themes.length === 0 ? null : themes;
if (!allowChildThemes && (!childThemes || childThemes.length === 0)) {
return null;
} }
let themes = [];
available.forEach(t => {
if (!childThemes || childThemes.indexOf(t) === -1) {
themes.push(t);
}
});
return themes.length === 0 ? null : themes;
}, },
@computed("allThemes", "allThemes.length", "model.component", "model") @computed("allThemes", "model.component", "model")
availableChildThemes(allThemes, count, component) { availableChildThemes(allThemes) {
if (count === 1 || component) { if (!this.get("model.component")) {
return null; const themeId = this.get("model.id");
return allThemes.filter(
theme => theme.get("id") !== themeId && theme.get("component")
);
} }
const themeId = this.get("model.id");
let themes = [];
allThemes.forEach(theme => {
if (themeId !== theme.get("id") && theme.get("component")) {
themes.push(theme);
}
});
return themes;
}, },
@computed("model.component") @computed("model.component")
@ -137,8 +96,11 @@ export default Ember.Controller.extend({
hasSettings(settings) { hasSettings(settings) {
return settings.length > 0; return settings.length > 0;
}, },
editedFieldsForTarget(target) {
downloadUrl: url("model.id", "/admin/themes/%@"), return this.get("model.editedFields").filter(
field => field.target === target
);
},
commitSwitchType() { commitSwitchType() {
const model = this.get("model"); const model = this.get("model");
@ -146,7 +108,6 @@ export default Ember.Controller.extend({
model.set("component", newValue); model.set("component", newValue);
if (newValue) { if (newValue) {
// component
this.set("parentController.currentTab", COMPONENTS); this.set("parentController.currentTab", COMPONENTS);
} else { } else {
this.set("parentController.currentTab", THEMES); this.set("parentController.currentTab", THEMES);
@ -166,8 +127,8 @@ export default Ember.Controller.extend({
}); });
this.get("parentController.model.content").forEach(theme => { this.get("parentController.model.content").forEach(theme => {
const children = Array.from(theme.get("childThemes")); const children = _.toArray(theme.get("childThemes"));
const rawChildren = Array.from(theme.get("child_themes") || []); const rawChildren = _.toArray(theme.get("child_themes") || []);
const index = children ? children.indexOf(model) : -1; const index = children ? children.indexOf(model) : -1;
if (index > -1) { if (index > -1) {
children.splice(index, 1); children.splice(index, 1);
@ -181,7 +142,14 @@ export default Ember.Controller.extend({
}) })
.catch(popupAjaxError); .catch(popupAjaxError);
}, },
transitionToEditRoute() {
this.transitionToRoute(
"adminCustomizeThemes.edit",
this.get("model.id"),
"common",
"scss"
);
},
actions: { actions: {
updateToLatest() { updateToLatest() {
this.set("updatingRemote", true); this.set("updatingRemote", true);
@ -238,25 +206,17 @@ export default Ember.Controller.extend({
}, },
editTheme() { editTheme() {
let edit = () =>
this.transitionToRoute(
this.get("editRouteName"),
this.get("model.id"),
"common",
"scss"
);
if (this.get("model.remote_theme")) { if (this.get("model.remote_theme")) {
bootbox.confirm( bootbox.confirm(
I18n.t("admin.customize.theme.edit_confirm"), I18n.t("admin.customize.theme.edit_confirm"),
result => { result => {
if (result) { if (result) {
edit(); this.transitionToEditRoute();
} }
} }
); );
} else { } else {
edit(); this.transitionToEditRoute();
} }
}, },

View File

@ -6,9 +6,13 @@ const THEME_UPLOAD_VAR = 2;
export const THEMES = "themes"; export const THEMES = "themes";
export const COMPONENTS = "components"; export const COMPONENTS = "components";
const SETTINGS_TYPE_ID = 5;
const Theme = RestModel.extend({ const Theme = RestModel.extend({
FIELDS_IDS: [0, 1], FIELDS_IDS: [0, 1],
isActive: Em.computed.or("default", "user_selectable"),
isPendingUpdates: Em.computed.gt("remote_theme.commits_behind", 0),
hasEditedFields: Em.computed.gt("editedFields.length", 0),
@computed("theme_fields") @computed("theme_fields")
themeFields(fields) { themeFields(fields) {
@ -43,9 +47,11 @@ const Theme = RestModel.extend({
); );
}, },
@computed("remote_theme", "remote_theme.commits_behind") @computed("theme_fields.@each")
isPendingUpdates(remote, commitsBehind) { editedFields(fields) {
return remote && commitsBehind && commitsBehind > 0; return fields.filter(
field => !Em.isBlank(field.value) && field.type_id !== SETTINGS_TYPE_ID
);
}, },
getKey(field) { getKey(field) {

View File

@ -35,5 +35,29 @@ export default Ember.Route.extend({
controller.setTargetName(wrapper.target || "common"); controller.setTargetName(wrapper.target || "common");
controller.set("fieldName", wrapper.field_name || "scss"); controller.set("fieldName", wrapper.field_name || "scss");
this.controllerFor("adminCustomizeThemes").set("editingTheme", true); this.controllerFor("adminCustomizeThemes").set("editingTheme", true);
this.set("shouldAlertUnsavedChanges", true);
},
actions: {
willTransition(transition) {
if (
this.get("controller.model.changed") &&
this.get("shouldAlertUnsavedChanges") &&
transition.intent.name !== this.routeName
) {
transition.abort();
bootbox.confirm(
I18n.t("admin.customize.theme.unsaved_changes_alert"),
I18n.t("admin.customize.theme.discard"),
I18n.t("admin.customize.theme.stay"),
result => {
if (!result) {
this.set("shouldAlertUnsavedChanges", false);
transition.retry();
}
}
);
}
}
} }
}); });

View File

@ -1,5 +1,25 @@
const externalResources = [
{
key: "admin.customize.theme.beginners_guide_title",
link: "https://meta.discourse.org/t/91966",
icon: "book"
},
{
key: "admin.customize.theme.developers_guide_title",
link: "https://meta.discourse.org/t/93648",
icon: "book"
},
{
key: "admin.customize.theme.browse_themes",
link: "https://meta.discourse.org/c/theme",
icon: "paint-brush"
}
];
export default Ember.Route.extend({ export default Ember.Route.extend({
setupController() { setupController(controller, model) {
this._super(...arguments);
this.controllerFor("adminCustomizeThemes").set("editingTheme", false); this.controllerFor("adminCustomizeThemes").set("editingTheme", false);
controller.set("externalResources", externalResources);
} }
}); });

View File

@ -37,9 +37,11 @@ export default Ember.Route.extend({
}, },
handleHighlight(theme) { handleHighlight(theme) {
this.get("controller.allThemes").forEach(t => t.set("active", false)); this.get("controller.allThemes")
.filter(t => t.get("selected"))
.forEach(t => t.set("selected", false));
if (theme) { if (theme) {
theme.set("active", true); theme.set("selected", true);
} }
}, },

View File

@ -1,4 +1,4 @@
{{#link-to 'adminCustomizeThemes.show' theme replace=true}} <div class="inner-wrapper">
{{plugin-outlet name="admin-customize-themes-list-item" connectorTagName='span' args=(hash theme=theme)}} {{plugin-outlet name="admin-customize-themes-list-item" connectorTagName='span' args=(hash theme=theme)}}
<div class="info"> <div class="info">
@ -7,28 +7,38 @@
</span> </span>
<span class="icons"> <span class="icons">
{{#if theme.default}} {{#unless theme.selected}}
{{d-icon "check" class="default-indicator" title="admin.customize.theme.default_theme_tooltip"}} {{#if theme.default}}
{{/if}} {{d-icon "check" class="default-indicator" title="admin.customize.theme.default_theme_tooltip"}}
{{#if theme.isPendingUpdates}} {{/if}}
{{d-icon "refresh" title="admin.customize.theme.updates_available_tooltip" class="light-grey-icon"}} {{#if theme.isPendingUpdates}}
{{/if}} {{d-icon "refresh" title="admin.customize.theme.updates_available_tooltip" class="light-grey-icon"}}
{{#if theme.isBroken}} {{/if}}
{{d-icon "exclamation-circle" class="broken-indicator" title="admin.customize.theme.broken_theme_tooltip"}} {{#if theme.isBroken}}
{{/if}} {{d-icon "exclamation-circle" class="broken-indicator" title="admin.customize.theme.broken_theme_tooltip"}}
{{/if}}
{{else}}
{{d-icon "caret-right"}}
{{/unless}}
</span> </span>
</div> </div>
{{#if hasComponents}} {{#if displayComponents}}
<div class="components-list"> <div class="components-list">
{{#each children as |child|}} {{#each children as |child|}}
<span class="component"> <span class="component">
{{child}} {{child}}
</span> </span>
{{/each}} {{/each}}
{{#if hasMore}} {{#if displayHasMore}}
<span class="others-count">{{I18n "admin.customize.theme.and_x_more" count=moreCount}}</span> <span {{action "toggleChildrenExpanded"}} class="others-count">
{{#if childrenExpanded}}
{{I18n "admin.customize.theme.collapse"}}
{{else}}
{{I18n "admin.customize.theme.and_x_more" count=moreCount}}
{{/if}}
</span>
{{/if}} {{/if}}
</div> </div>
{{/if}} {{/if}}
{{/link-to}} </div>

View File

@ -7,16 +7,16 @@
</div> </div>
</div> </div>
<div class="themes-list-container" style="max-height: 485px;"> <div class="themes-list-container" style="max-height: 450px;">
{{#if hasThemes}} {{#if hasThemes}}
{{#if componentsTabActive}} {{#if componentsTabActive}}
{{#each themesList as |theme|}} {{#each themesList as |theme|}}
{{themes-list-item theme=theme}} {{themes-list-item theme=theme navigateToTheme=(action "navigateToTheme" theme)}}
{{/each}} {{/each}}
{{else}} {{else}}
{{#if hasUserThemes}} {{#if hasUserThemes}}
{{#each userThemes as |theme|}} {{#each userThemes as |theme|}}
{{themes-list-item theme=theme}} {{themes-list-item theme=theme navigateToTheme=(action "navigateToTheme" theme)}}
{{/each}} {{/each}}
{{#if hasInactiveThemes}} {{#if hasInactiveThemes}}
@ -28,7 +28,7 @@
{{#if hasInactiveThemes}} {{#if hasInactiveThemes}}
{{#each inactiveThemes as |theme|}} {{#each inactiveThemes as |theme|}}
{{themes-list-item theme=theme}} {{themes-list-item theme=theme navigateToTheme=(action "navigateToTheme" theme)}}
{{/each}} {{/each}}
{{/if}} {{/if}}
{{/if}} {{/if}}

View File

@ -0,0 +1,11 @@
<div class="themes-intro">
<h1>{{I18n "admin.customize.theme.themes_intro"}}</h1>
<div class="external-resources">
{{#each externalResources as |resource|}}
<a href={{resource.link}} class="external-link" target="_blank">
{{d-icon resource.icon}}
{{I18n resource.key}}
</a>
{{/each}}
</div>
</div>

View File

@ -52,11 +52,11 @@
<div class="control-unit"> <div class="control-unit">
<div class="mini-title">{{i18n "admin.customize.theme.css_html"}}</div> <div class="mini-title">{{i18n "admin.customize.theme.css_html"}}</div>
{{#if hasEditedFields}} {{#if model.hasEditedFields}}
<div class="description">{{i18n "admin.customize.theme.custom_sections"}}</div> <div class="description">{{i18n "admin.customize.theme.custom_sections"}}</div>
<ul> <ul>
{{#each editedDescriptions as |desc|}} {{#each editedFieldsFormatted as |field|}}
<li>{{desc}}</li> <li>{{field}}</li>
{{/each}} {{/each}}
</ul> </ul>
{{else}} {{else}}
@ -127,24 +127,17 @@
{{#if availableChildThemes}} {{#if availableChildThemes}}
<div class="control-unit"> <div class="control-unit">
<div class="mini-title">{{i18n "admin.customize.theme.theme_components"}}</div> <div class="mini-title">{{i18n "admin.customize.theme.theme_components"}}</div>
{{#unless model.childThemes.length}} {{#if model.childThemes.length}}
<div class="description"> <ul class='removable-list'>
<label class='checkbox-label'> {{#each model.childThemes as |child|}}
{{input type="checkbox" checked=allowChildThemes}} <li>{{#link-to 'adminCustomizeThemes.show' child replace=true class='col'}}{{child.name}}{{/link-to}} {{d-button action="removeChildTheme" actionParam=child class="btn-small cancel-edit col" icon="times"}}</li>
{{i18n "admin.customize.theme.child_themes_check"}} {{/each}}
</label> </ul>
</div> {{/if}}
{{else}}
<ul class='removable-list'>
{{#each model.childThemes as |child|}}
<li>{{#link-to 'adminCustomizeThemes.show' child replace=true class='col'}}{{child.name}}{{/link-to}} {{d-button action="removeChildTheme" actionParam=child class="btn-small cancel-edit col" icon="times"}}</li>
{{/each}}
</ul>
{{/unless}}
{{#if selectableChildThemes}} {{#if selectableChildThemes}}
<div class="description"> <div class="description">
{{combo-box forceEscape=true filterable=true content=selectableChildThemes value=selectedChildThemeId}} {{combo-box forceEscape=true filterable=true content=selectableChildThemes value=selectedChildThemeId none="admin.customize.theme.select_component"}}
{{#d-button action="addChildTheme" icon="plus"}}{{i18n "admin.customize.theme.add"}}{{/d-button}} {{#d-button action="addChildTheme" icon="plus" disabled=addButtonDisabled class="add-component-button"}}{{i18n "admin.customize.theme.add"}}{{/d-button}}
</div> </div>
{{/if}} {{/if}}
</div> </div>

View File

@ -59,6 +59,18 @@
} }
.admin-customize.admin-customize-themes { .admin-customize.admin-customize-themes {
.themes-intro {
float: right;
width: 34%;
margin-right: 20%;
margin-top: 60px;
.external-link {
display: block;
margin-bottom: 5px;
}
}
.customize-themes-header { .customize-themes-header {
border-bottom: 1px solid $primary-low; border-bottom: 1px solid $primary-low;
padding-bottom: 8px; padding-bottom: 8px;
@ -158,12 +170,15 @@
float: left; float: left;
width: 70%; width: 70%;
} }
.add-component-button {
vertical-align: middle;
}
.themes-list { .themes-list {
border-right: 1px solid $primary-low; border-right: 1px solid $primary-low;
border-bottom: 1px solid $primary-low; border-bottom: 1px solid $primary-low;
float: left; float: left;
width: 28%; width: 28%;
overflow: hidden;
} }
.themes-list-header { .themes-list-header {
@ -180,8 +195,7 @@
&.active { &.active {
font-weight: bold; font-weight: bold;
color: $secondary; color: $tertiary;
background-color: $tertiary;
} }
&:not(.active) { &:not(.active) {
@ -195,18 +209,10 @@
} }
.themes-list-container { .themes-list-container {
max-height: 485px; overflow-y: scroll;
overflow-y: auto; box-sizing: content-box;
width: 100%; /* overridden in javascript to hide scrollbar */
&::-webkit-scrollbar-track {
background-color: $secondary;
}
&::-webkit-scrollbar {
width: 5px;
}
&::-webkit-scrollbar-thumb {
background-color: $primary-low;
}
.themes-list-item:last-child { .themes-list-item:last-child {
border-bottom: none; border-bottom: none;
} }
@ -216,22 +222,47 @@
display: flex; display: flex;
border-left: 1px solid $primary-low; border-left: 1px solid $primary-low;
&:not(.inactive-indicator):not(.active):hover { &.inactive-indicator {
border-right: 0;
border-left: 0;
font-weight: bold;
color: $primary-medium;
span.empty {
padding-left: 5px;
padding-top: 15px;
}
}
&:not(.inactive-indicator):not(.selected):hover {
background-color: $tertiary-low; background-color: $tertiary-low;
.component { .component {
border-color: $primary-low-mid; border-color: $primary-low-mid;
} }
} }
&.active {
&.selected {
color: $secondary; color: $secondary;
background-color: $tertiary; background-color: $tertiary;
.fa { .fa {
color: inherit; color: inherit;
} }
} }
&:not(.selected) {
.broken-indicator {
color: $danger;
}
.fa {
opacity: 0.7;
}
.default-indicator {
color: $success;
}
}
.light-grey-icon { .light-grey-icon {
color: $primary-medium; color: $primary-medium;
} }
.info { .info {
overflow: hidden; overflow: hidden;
font-weight: bold; font-weight: bold;
@ -253,6 +284,9 @@
font-size: $font-down-1; font-size: $font-down-1;
align-items: baseline; align-items: baseline;
.others-count:hover {
text-decoration: underline;
}
.component { .component {
display: flex; display: flex;
padding: 3px 5px 3px 5px; padding: 3px 5px 3px 5px;
@ -262,37 +296,17 @@
margin-bottom: 5px; margin-bottom: 5px;
} }
} }
&:not(.active) {
.broken-indicator {
color: $danger;
}
.default-indicator { .inner-wrapper {
color: $success;
}
}
a {
padding: 10px; padding: 10px;
} cursor: pointer;
&.inactive-indicator {
border-right: 0;
border-left: 0;
font-weight: bold;
color: $primary-medium;
span.empty {
padding-left: 5px;
padding-top: 15px;
}
} }
span.empty { span.empty {
padding: 3px 10px 3px 10px; padding: 3px 10px 3px 10px;
} }
a, .inner-wrapper,
span.empty { span.empty {
color: inherit; color: inherit;
width: 100%; width: 100%;

View File

@ -3191,7 +3191,6 @@ en:
import: "Import" import: "Import"
delete: "Delete" delete: "Delete"
delete_confirm: "Delete this theme?" delete_confirm: "Delete this theme?"
about: "Modify CSS stylesheets and HTML headers on the site. Add a customization to start."
color: "Color" color: "Color"
opacity: "Opacity" opacity: "Opacity"
copy: "Copy" copy: "Copy"
@ -3214,6 +3213,10 @@ en:
components: "Components" components: "Components"
theme_name: "Theme name" theme_name: "Theme name"
component_name: "Component name" component_name: "Component name"
themes_intro: "Select an existing theme or create a new one to get started"
beginners_guide_title: "Beginners guide to using Discourse Themes"
developers_guide_title: "Developers guide to Discourse Themes"
browse_themes: "Browse community themes"
import_theme: "Import Theme" import_theme: "Import Theme"
customize_desc: "Customize:" customize_desc: "Customize:"
title: "Themes" title: "Themes"
@ -3245,6 +3248,7 @@ en:
default_theme_tooltip: "This theme is the site's default theme" default_theme_tooltip: "This theme is the site's default theme"
updates_available_tooltip: "Updates are available for this theme" updates_available_tooltip: "Updates are available for this theme"
and_x_more: "and {{count}} more." and_x_more: "and {{count}} more."
collapse: Collapse
uploads: "Uploads" uploads: "Uploads"
no_uploads: "You can upload assets associated with your theme such as fonts and images" no_uploads: "You can upload assets associated with your theme such as fonts and images"
add_upload: "Add Upload" add_upload: "Add Upload"
@ -3256,7 +3260,10 @@ en:
no_overwrite: "Invalid variable name. Must not overwrite an existing variable." no_overwrite: "Invalid variable name. Must not overwrite an existing variable."
must_be_unique: "Invalid variable name. Must be unique." must_be_unique: "Invalid variable name. Must be unique."
upload: "Upload" upload: "Upload"
child_themes_check: "Theme includes other child themes" select_component: "Select a component..."
unsaved_changes_alert: "You haven't saved your changes yet, do you want to discard them and move on?"
discard: "Discard"
stay: "Stay"
css_html: "Custom CSS/HTML" css_html: "Custom CSS/HTML"
edit_css_html: "Edit CSS/HTML" edit_css_html: "Edit CSS/HTML"
edit_css_html_help: "You have not edited any CSS or HTML" edit_css_html_help: "You have not edited any CSS or HTML"

View File

@ -62,7 +62,7 @@ componentTest("with children", {
beforeEach() { beforeEach() {
this.set( this.set(
"theme", "theme",
Theme.create({ name: "Test", childThemes: childrenList }) Theme.create({ name: "Test", childThemes: childrenList, default: true })
); );
}, },