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:
parent
a4f057a589
commit
ca28548762
|
@ -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;
|
||||
|
||||
export default Ember.Component.extend({
|
||||
childrenExpanded: false,
|
||||
classNames: ["themes-list-item"],
|
||||
classNameBindings: ["theme.active:active"],
|
||||
classNameBindings: ["theme.selected:selected"],
|
||||
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(
|
||||
"theme.component",
|
||||
"theme.childThemes.@each.name",
|
||||
"theme.childThemes.length"
|
||||
"theme.childThemes.length",
|
||||
"childrenExpanded"
|
||||
)
|
||||
children() {
|
||||
const theme = this.get("theme");
|
||||
const children = theme.get("childThemes");
|
||||
let children = theme.get("childThemes");
|
||||
if (theme.get("component") || !children) {
|
||||
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")
|
||||
moreCount(childrenCount, component) {
|
||||
if (component || !childrenCount) {
|
||||
@computed(
|
||||
"theme.childThemes.length",
|
||||
"theme.component",
|
||||
"childrenExpanded",
|
||||
"children.length"
|
||||
)
|
||||
moreCount(childrenCount, component, expanded) {
|
||||
if (component || !childrenCount || expanded) {
|
||||
return 0;
|
||||
}
|
||||
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");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { THEMES, COMPONENTS } from "admin/models/theme";
|
||||
import { default as computed } from "ember-addons/ember-computed-decorators";
|
||||
|
||||
const NUM_ENTRIES = 8;
|
||||
const MAX_LIST_HEIGHT = 700;
|
||||
|
||||
export default Ember.Component.extend({
|
||||
THEMES: THEMES,
|
||||
|
@ -63,15 +63,24 @@ export default Ember.Component.extend({
|
|||
},
|
||||
|
||||
didRender() {
|
||||
let height = -1;
|
||||
this.$(".themes-list-item")
|
||||
.slice(0, NUM_ENTRIES)
|
||||
.each(function() {
|
||||
height += $(this).outerHeight();
|
||||
});
|
||||
if (height >= 485 && height <= 800) {
|
||||
this.$(".themes-list-container").css("max-height", `${height}px`);
|
||||
this._super(...arguments);
|
||||
|
||||
// hide scrollbar
|
||||
const $container = this.$(".themes-list-container");
|
||||
const containerNode = $container[0];
|
||||
if (containerNode) {
|
||||
const width = containerNode.offsetWidth - containerNode.clientWidth;
|
||||
$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: {
|
||||
|
@ -79,6 +88,11 @@ export default Ember.Component.extend({
|
|||
if (newTab !== this.get("currentTab")) {
|
||||
this.set("currentTab", newTab);
|
||||
}
|
||||
},
|
||||
navigateToTheme(theme) {
|
||||
Em.getOwner(this)
|
||||
.lookup("router:main")
|
||||
.transitionTo("adminCustomizeThemes.show", theme);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -5,8 +5,10 @@ import {
|
|||
} from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
maximized: false,
|
||||
section: null,
|
||||
currentTarget: 0,
|
||||
maximized: false,
|
||||
previewUrl: url("model.id", "/admin/themes/%@/preview"),
|
||||
|
||||
editRouteName: "adminCustomizeThemes.edit",
|
||||
|
||||
|
@ -86,8 +88,6 @@ export default Ember.Controller.extend({
|
|||
return this.get("model").hasEdited(target);
|
||||
},
|
||||
|
||||
currentTarget: 0,
|
||||
|
||||
setTargetName: function(name) {
|
||||
const target = this.get("targets").find(t => t.name === name);
|
||||
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() {
|
||||
return this.get("maximized") ? "compress" : "expand";
|
||||
}.property("maximized"),
|
||||
@computed("model.isSaving")
|
||||
saveButtonText(isSaving) {
|
||||
return isSaving ? I18n.t("saving") : I18n.t("admin.customize.save");
|
||||
},
|
||||
|
||||
saveButtonText: function() {
|
||||
return this.get("model.isSaving")
|
||||
? I18n.t("saving")
|
||||
: I18n.t("admin.customize.save");
|
||||
}.property("model.isSaving"),
|
||||
|
||||
saveDisabled: function() {
|
||||
return !this.get("model.changed") || this.get("model.isSaving");
|
||||
}.property("model.changed", "model.isSaving"),
|
||||
@computed("model.changed", "model.isSaving")
|
||||
saveDisabled(changed, isSaving) {
|
||||
return !changed || isSaving;
|
||||
},
|
||||
|
||||
actions: {
|
||||
save() {
|
||||
|
|
|
@ -9,106 +9,65 @@ import ThemeSettings from "admin/models/theme-settings";
|
|||
import { THEMES, COMPONENTS } from "admin/models/theme";
|
||||
|
||||
const THEME_UPLOAD_VAR = 2;
|
||||
const SETTINGS_TYPE_ID = 5;
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
editRouteName: "adminCustomizeThemes.edit",
|
||||
|
||||
@observes("allowChildThemes")
|
||||
setSelectedThemeId() {
|
||||
const available = this.get("selectableChildThemes");
|
||||
if (
|
||||
!this.get("selectedChildThemeId") &&
|
||||
available &&
|
||||
available.length > 0
|
||||
) {
|
||||
this.set("selectedChildThemeId", available[0].get("id"));
|
||||
}
|
||||
},
|
||||
downloadUrl: url("model.id", "/admin/themes/%@"),
|
||||
previewUrl: url("model.id", "/admin/themes/%@/preview"),
|
||||
addButtonDisabled: Em.computed.empty("selectedChildThemeId"),
|
||||
|
||||
@computed("model", "allThemes", "model.component")
|
||||
parentThemes(model, allThemes) {
|
||||
if (!model.get("component")) {
|
||||
return null;
|
||||
}
|
||||
let parents = allThemes.filter(theme =>
|
||||
const parents = allThemes.filter(theme =>
|
||||
_.contains(theme.get("childThemes"), model)
|
||||
);
|
||||
return parents.length === 0 ? null : parents;
|
||||
},
|
||||
|
||||
@computed("model.theme_fields.@each")
|
||||
hasEditedFields(fields) {
|
||||
return fields.any(
|
||||
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(" , ");
|
||||
}
|
||||
};
|
||||
@computed("model.editedFields")
|
||||
editedFieldsFormatted(fields) {
|
||||
const descriptions = [];
|
||||
["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")
|
||||
colorSchemeChanged(colorSchemeId, existingId) {
|
||||
colorSchemeId = colorSchemeId === null ? null : parseInt(colorSchemeId);
|
||||
return colorSchemeId !== existingId;
|
||||
},
|
||||
|
||||
@computed(
|
||||
"availableChildThemes",
|
||||
"model.childThemes.@each",
|
||||
"model",
|
||||
"allowChildThemes"
|
||||
)
|
||||
selectableChildThemes(available, childThemes, allowChildThemes) {
|
||||
if (!allowChildThemes && (!childThemes || childThemes.length === 0)) {
|
||||
return null;
|
||||
@computed("availableChildThemes", "model.childThemes.@each", "model")
|
||||
selectableChildThemes(available, childThemes) {
|
||||
if (available) {
|
||||
const themes = !childThemes
|
||||
? available
|
||||
: available.filter(theme => childThemes.indexOf(theme) === -1);
|
||||
return themes.length === 0 ? null : themes;
|
||||
}
|
||||
|
||||
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")
|
||||
availableChildThemes(allThemes, count, component) {
|
||||
if (count === 1 || component) {
|
||||
return null;
|
||||
@computed("allThemes", "model.component", "model")
|
||||
availableChildThemes(allThemes) {
|
||||
if (!this.get("model.component")) {
|
||||
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")
|
||||
|
@ -137,8 +96,11 @@ export default Ember.Controller.extend({
|
|||
hasSettings(settings) {
|
||||
return settings.length > 0;
|
||||
},
|
||||
|
||||
downloadUrl: url("model.id", "/admin/themes/%@"),
|
||||
editedFieldsForTarget(target) {
|
||||
return this.get("model.editedFields").filter(
|
||||
field => field.target === target
|
||||
);
|
||||
},
|
||||
|
||||
commitSwitchType() {
|
||||
const model = this.get("model");
|
||||
|
@ -146,7 +108,6 @@ export default Ember.Controller.extend({
|
|||
model.set("component", newValue);
|
||||
|
||||
if (newValue) {
|
||||
// component
|
||||
this.set("parentController.currentTab", COMPONENTS);
|
||||
} else {
|
||||
this.set("parentController.currentTab", THEMES);
|
||||
|
@ -166,8 +127,8 @@ export default Ember.Controller.extend({
|
|||
});
|
||||
|
||||
this.get("parentController.model.content").forEach(theme => {
|
||||
const children = Array.from(theme.get("childThemes"));
|
||||
const rawChildren = Array.from(theme.get("child_themes") || []);
|
||||
const children = _.toArray(theme.get("childThemes"));
|
||||
const rawChildren = _.toArray(theme.get("child_themes") || []);
|
||||
const index = children ? children.indexOf(model) : -1;
|
||||
if (index > -1) {
|
||||
children.splice(index, 1);
|
||||
|
@ -181,7 +142,14 @@ export default Ember.Controller.extend({
|
|||
})
|
||||
.catch(popupAjaxError);
|
||||
},
|
||||
|
||||
transitionToEditRoute() {
|
||||
this.transitionToRoute(
|
||||
"adminCustomizeThemes.edit",
|
||||
this.get("model.id"),
|
||||
"common",
|
||||
"scss"
|
||||
);
|
||||
},
|
||||
actions: {
|
||||
updateToLatest() {
|
||||
this.set("updatingRemote", true);
|
||||
|
@ -238,25 +206,17 @@ export default Ember.Controller.extend({
|
|||
},
|
||||
|
||||
editTheme() {
|
||||
let edit = () =>
|
||||
this.transitionToRoute(
|
||||
this.get("editRouteName"),
|
||||
this.get("model.id"),
|
||||
"common",
|
||||
"scss"
|
||||
);
|
||||
|
||||
if (this.get("model.remote_theme")) {
|
||||
bootbox.confirm(
|
||||
I18n.t("admin.customize.theme.edit_confirm"),
|
||||
result => {
|
||||
if (result) {
|
||||
edit();
|
||||
this.transitionToEditRoute();
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
edit();
|
||||
this.transitionToEditRoute();
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -6,9 +6,13 @@ const THEME_UPLOAD_VAR = 2;
|
|||
|
||||
export const THEMES = "themes";
|
||||
export const COMPONENTS = "components";
|
||||
const SETTINGS_TYPE_ID = 5;
|
||||
|
||||
const Theme = RestModel.extend({
|
||||
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")
|
||||
themeFields(fields) {
|
||||
|
@ -43,9 +47,11 @@ const Theme = RestModel.extend({
|
|||
);
|
||||
},
|
||||
|
||||
@computed("remote_theme", "remote_theme.commits_behind")
|
||||
isPendingUpdates(remote, commitsBehind) {
|
||||
return remote && commitsBehind && commitsBehind > 0;
|
||||
@computed("theme_fields.@each")
|
||||
editedFields(fields) {
|
||||
return fields.filter(
|
||||
field => !Em.isBlank(field.value) && field.type_id !== SETTINGS_TYPE_ID
|
||||
);
|
||||
},
|
||||
|
||||
getKey(field) {
|
||||
|
|
|
@ -35,5 +35,29 @@ export default Ember.Route.extend({
|
|||
controller.setTargetName(wrapper.target || "common");
|
||||
controller.set("fieldName", wrapper.field_name || "scss");
|
||||
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();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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({
|
||||
setupController() {
|
||||
setupController(controller, model) {
|
||||
this._super(...arguments);
|
||||
this.controllerFor("adminCustomizeThemes").set("editingTheme", false);
|
||||
controller.set("externalResources", externalResources);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -37,9 +37,11 @@ export default Ember.Route.extend({
|
|||
},
|
||||
|
||||
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) {
|
||||
theme.set("active", true);
|
||||
theme.set("selected", true);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -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)}}
|
||||
|
||||
<div class="info">
|
||||
|
@ -7,28 +7,38 @@
|
|||
</span>
|
||||
|
||||
<span class="icons">
|
||||
{{#if theme.default}}
|
||||
{{d-icon "check" class="default-indicator" title="admin.customize.theme.default_theme_tooltip"}}
|
||||
{{/if}}
|
||||
{{#if theme.isPendingUpdates}}
|
||||
{{d-icon "refresh" title="admin.customize.theme.updates_available_tooltip" class="light-grey-icon"}}
|
||||
{{/if}}
|
||||
{{#if theme.isBroken}}
|
||||
{{d-icon "exclamation-circle" class="broken-indicator" title="admin.customize.theme.broken_theme_tooltip"}}
|
||||
{{/if}}
|
||||
{{#unless theme.selected}}
|
||||
{{#if theme.default}}
|
||||
{{d-icon "check" class="default-indicator" title="admin.customize.theme.default_theme_tooltip"}}
|
||||
{{/if}}
|
||||
{{#if theme.isPendingUpdates}}
|
||||
{{d-icon "refresh" title="admin.customize.theme.updates_available_tooltip" class="light-grey-icon"}}
|
||||
{{/if}}
|
||||
{{#if theme.isBroken}}
|
||||
{{d-icon "exclamation-circle" class="broken-indicator" title="admin.customize.theme.broken_theme_tooltip"}}
|
||||
{{/if}}
|
||||
{{else}}
|
||||
{{d-icon "caret-right"}}
|
||||
{{/unless}}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{{#if hasComponents}}
|
||||
{{#if displayComponents}}
|
||||
<div class="components-list">
|
||||
{{#each children as |child|}}
|
||||
<span class="component">
|
||||
{{child}}
|
||||
</span>
|
||||
{{/each}}
|
||||
{{#if hasMore}}
|
||||
<span class="others-count">{{I18n "admin.customize.theme.and_x_more" count=moreCount}}</span>
|
||||
{{#if displayHasMore}}
|
||||
<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}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/link-to}}
|
||||
</div>
|
||||
|
|
|
@ -7,16 +7,16 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="themes-list-container" style="max-height: 485px;">
|
||||
<div class="themes-list-container" style="max-height: 450px;">
|
||||
{{#if hasThemes}}
|
||||
{{#if componentsTabActive}}
|
||||
{{#each themesList as |theme|}}
|
||||
{{themes-list-item theme=theme}}
|
||||
{{themes-list-item theme=theme navigateToTheme=(action "navigateToTheme" theme)}}
|
||||
{{/each}}
|
||||
{{else}}
|
||||
{{#if hasUserThemes}}
|
||||
{{#each userThemes as |theme|}}
|
||||
{{themes-list-item theme=theme}}
|
||||
{{themes-list-item theme=theme navigateToTheme=(action "navigateToTheme" theme)}}
|
||||
{{/each}}
|
||||
|
||||
{{#if hasInactiveThemes}}
|
||||
|
@ -28,7 +28,7 @@
|
|||
|
||||
{{#if hasInactiveThemes}}
|
||||
{{#each inactiveThemes as |theme|}}
|
||||
{{themes-list-item theme=theme}}
|
||||
{{themes-list-item theme=theme navigateToTheme=(action "navigateToTheme" theme)}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
|
|
@ -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>
|
|
@ -52,11 +52,11 @@
|
|||
|
||||
<div class="control-unit">
|
||||
<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>
|
||||
<ul>
|
||||
{{#each editedDescriptions as |desc|}}
|
||||
<li>{{desc}}</li>
|
||||
{{#each editedFieldsFormatted as |field|}}
|
||||
<li>{{field}}</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{else}}
|
||||
|
@ -127,24 +127,17 @@
|
|||
{{#if availableChildThemes}}
|
||||
<div class="control-unit">
|
||||
<div class="mini-title">{{i18n "admin.customize.theme.theme_components"}}</div>
|
||||
{{#unless model.childThemes.length}}
|
||||
<div class="description">
|
||||
<label class='checkbox-label'>
|
||||
{{input type="checkbox" checked=allowChildThemes}}
|
||||
{{i18n "admin.customize.theme.child_themes_check"}}
|
||||
</label>
|
||||
</div>
|
||||
{{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 model.childThemes.length}}
|
||||
<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>
|
||||
{{/if}}
|
||||
{{#if selectableChildThemes}}
|
||||
<div class="description">
|
||||
{{combo-box forceEscape=true filterable=true content=selectableChildThemes value=selectedChildThemeId}}
|
||||
{{#d-button action="addChildTheme" icon="plus"}}{{i18n "admin.customize.theme.add"}}{{/d-button}}
|
||||
{{combo-box forceEscape=true filterable=true content=selectableChildThemes value=selectedChildThemeId none="admin.customize.theme.select_component"}}
|
||||
{{#d-button action="addChildTheme" icon="plus" disabled=addButtonDisabled class="add-component-button"}}{{i18n "admin.customize.theme.add"}}{{/d-button}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
|
|
@ -59,6 +59,18 @@
|
|||
}
|
||||
|
||||
.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 {
|
||||
border-bottom: 1px solid $primary-low;
|
||||
padding-bottom: 8px;
|
||||
|
@ -158,12 +170,15 @@
|
|||
float: left;
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
.add-component-button {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.themes-list {
|
||||
border-right: 1px solid $primary-low;
|
||||
border-bottom: 1px solid $primary-low;
|
||||
float: left;
|
||||
width: 28%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.themes-list-header {
|
||||
|
@ -180,8 +195,7 @@
|
|||
|
||||
&.active {
|
||||
font-weight: bold;
|
||||
color: $secondary;
|
||||
background-color: $tertiary;
|
||||
color: $tertiary;
|
||||
}
|
||||
|
||||
&:not(.active) {
|
||||
|
@ -195,18 +209,10 @@
|
|||
}
|
||||
|
||||
.themes-list-container {
|
||||
max-height: 485px;
|
||||
overflow-y: auto;
|
||||
overflow-y: scroll;
|
||||
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 {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
@ -216,22 +222,47 @@
|
|||
display: flex;
|
||||
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;
|
||||
.component {
|
||||
border-color: $primary-low-mid;
|
||||
}
|
||||
}
|
||||
&.active {
|
||||
|
||||
&.selected {
|
||||
color: $secondary;
|
||||
background-color: $tertiary;
|
||||
.fa {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
&:not(.selected) {
|
||||
.broken-indicator {
|
||||
color: $danger;
|
||||
}
|
||||
.fa {
|
||||
opacity: 0.7;
|
||||
}
|
||||
.default-indicator {
|
||||
color: $success;
|
||||
}
|
||||
}
|
||||
|
||||
.light-grey-icon {
|
||||
color: $primary-medium;
|
||||
}
|
||||
|
||||
.info {
|
||||
overflow: hidden;
|
||||
font-weight: bold;
|
||||
|
@ -253,6 +284,9 @@
|
|||
font-size: $font-down-1;
|
||||
align-items: baseline;
|
||||
|
||||
.others-count:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.component {
|
||||
display: flex;
|
||||
padding: 3px 5px 3px 5px;
|
||||
|
@ -262,37 +296,17 @@
|
|||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
&:not(.active) {
|
||||
.broken-indicator {
|
||||
color: $danger;
|
||||
}
|
||||
|
||||
.default-indicator {
|
||||
color: $success;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
.inner-wrapper {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
&.inactive-indicator {
|
||||
border-right: 0;
|
||||
border-left: 0;
|
||||
font-weight: bold;
|
||||
color: $primary-medium;
|
||||
|
||||
span.empty {
|
||||
padding-left: 5px;
|
||||
padding-top: 15px;
|
||||
}
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
span.empty {
|
||||
padding: 3px 10px 3px 10px;
|
||||
}
|
||||
|
||||
a,
|
||||
.inner-wrapper,
|
||||
span.empty {
|
||||
color: inherit;
|
||||
width: 100%;
|
||||
|
|
|
@ -3191,7 +3191,6 @@ en:
|
|||
import: "Import"
|
||||
delete: "Delete"
|
||||
delete_confirm: "Delete this theme?"
|
||||
about: "Modify CSS stylesheets and HTML headers on the site. Add a customization to start."
|
||||
color: "Color"
|
||||
opacity: "Opacity"
|
||||
copy: "Copy"
|
||||
|
@ -3214,6 +3213,10 @@ en:
|
|||
components: "Components"
|
||||
theme_name: "Theme name"
|
||||
component_name: "Component name"
|
||||
themes_intro: "Select an existing theme or create a new one to get started"
|
||||
beginners_guide_title: "Beginner’s guide to using Discourse Themes"
|
||||
developers_guide_title: "Developer’s guide to Discourse Themes"
|
||||
browse_themes: "Browse community themes"
|
||||
import_theme: "Import Theme"
|
||||
customize_desc: "Customize:"
|
||||
title: "Themes"
|
||||
|
@ -3245,6 +3248,7 @@ en:
|
|||
default_theme_tooltip: "This theme is the site's default theme"
|
||||
updates_available_tooltip: "Updates are available for this theme"
|
||||
and_x_more: "and {{count}} more."
|
||||
collapse: Collapse
|
||||
uploads: "Uploads"
|
||||
no_uploads: "You can upload assets associated with your theme such as fonts and images"
|
||||
add_upload: "Add Upload"
|
||||
|
@ -3256,7 +3260,10 @@ en:
|
|||
no_overwrite: "Invalid variable name. Must not overwrite an existing variable."
|
||||
must_be_unique: "Invalid variable name. Must be unique."
|
||||
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"
|
||||
edit_css_html: "Edit CSS/HTML"
|
||||
edit_css_html_help: "You have not edited any CSS or HTML"
|
||||
|
|
|
@ -62,7 +62,7 @@ componentTest("with children", {
|
|||
beforeEach() {
|
||||
this.set(
|
||||
"theme",
|
||||
Theme.create({ name: "Test", childThemes: childrenList })
|
||||
Theme.create({ name: "Test", childThemes: childrenList, default: true })
|
||||
);
|
||||
},
|
||||
|
||||
|
|
Loading…
Reference in New Issue