From ca28548762d2e6a7c34293763f2831edc62046a2 Mon Sep 17 00:00:00 2001 From: OsamaSayegh Date: Thu, 6 Sep 2018 21:56:00 +0300 Subject: [PATCH] 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 --- .../admin/components/themes-list-item.js.es6 | 122 ++++++++++++++-- .../admin/components/themes-list.js.es6 | 32 ++-- .../admin-customize-themes-edit.js.es6 | 31 ++-- .../admin-customize-themes-show.js.es6 | 138 +++++++----------- .../javascripts/admin/models/theme.js.es6 | 12 +- .../routes/admin-customize-themes-edit.js.es6 | 24 +++ .../admin-customize-themes-index.js.es6 | 22 ++- .../routes/admin-customize-themes-show.js.es6 | 6 +- .../templates/components/themes-list-item.hbs | 38 +++-- .../templates/components/themes-list.hbs | 8 +- .../templates/customize-themes-index.hbs | 11 ++ .../admin/templates/customize-themes-show.hbs | 31 ++-- .../stylesheets/common/admin/customize.scss | 92 +++++++----- config/locales/client.en.yml | 11 +- .../components/themes-list-item-test.js.es6 | 2 +- 15 files changed, 372 insertions(+), 208 deletions(-) create mode 100644 app/assets/javascripts/admin/templates/customize-themes-index.hbs diff --git a/app/assets/javascripts/admin/components/themes-list-item.js.es6 b/app/assets/javascripts/admin/components/themes-list-item.js.es6 index fe5243585b..44f337dd76 100644 --- a/app/assets/javascripts/admin/components/themes-list-item.js.es6 +++ b/app/assets/javascripts/admin/components/themes-list-item.js.es6 @@ -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"); + } } }); diff --git a/app/assets/javascripts/admin/components/themes-list.js.es6 b/app/assets/javascripts/admin/components/themes-list.js.es6 index cf41e4dd33..c9498566c3 100644 --- a/app/assets/javascripts/admin/components/themes-list.js.es6 +++ b/app/assets/javascripts/admin/components/themes-list.js.es6 @@ -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); } } }); diff --git a/app/assets/javascripts/admin/controllers/admin-customize-themes-edit.js.es6 b/app/assets/javascripts/admin/controllers/admin-customize-themes-edit.js.es6 index 7cbe893fe5..c8082cc84d 100644 --- a/app/assets/javascripts/admin/controllers/admin-customize-themes-edit.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-customize-themes-edit.js.es6 @@ -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() { diff --git a/app/assets/javascripts/admin/controllers/admin-customize-themes-show.js.es6 b/app/assets/javascripts/admin/controllers/admin-customize-themes-show.js.es6 index c73d4467fd..47b59317f1 100644 --- a/app/assets/javascripts/admin/controllers/admin-customize-themes-show.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-customize-themes-show.js.es6 @@ -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(); } }, diff --git a/app/assets/javascripts/admin/models/theme.js.es6 b/app/assets/javascripts/admin/models/theme.js.es6 index fa37798fc0..413d9a93c6 100644 --- a/app/assets/javascripts/admin/models/theme.js.es6 +++ b/app/assets/javascripts/admin/models/theme.js.es6 @@ -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) { diff --git a/app/assets/javascripts/admin/routes/admin-customize-themes-edit.js.es6 b/app/assets/javascripts/admin/routes/admin-customize-themes-edit.js.es6 index 7b63658ac0..5bab290091 100644 --- a/app/assets/javascripts/admin/routes/admin-customize-themes-edit.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-customize-themes-edit.js.es6 @@ -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(); + } + } + ); + } + } } }); diff --git a/app/assets/javascripts/admin/routes/admin-customize-themes-index.js.es6 b/app/assets/javascripts/admin/routes/admin-customize-themes-index.js.es6 index e1a5bf51db..e29ef8659e 100644 --- a/app/assets/javascripts/admin/routes/admin-customize-themes-index.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-customize-themes-index.js.es6 @@ -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); } }); diff --git a/app/assets/javascripts/admin/routes/admin-customize-themes-show.js.es6 b/app/assets/javascripts/admin/routes/admin-customize-themes-show.js.es6 index f3736ffa74..f39b747ef7 100644 --- a/app/assets/javascripts/admin/routes/admin-customize-themes-show.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-customize-themes-show.js.es6 @@ -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); } }, diff --git a/app/assets/javascripts/admin/templates/components/themes-list-item.hbs b/app/assets/javascripts/admin/templates/components/themes-list-item.hbs index 3cde5084b0..a12ddb4123 100644 --- a/app/assets/javascripts/admin/templates/components/themes-list-item.hbs +++ b/app/assets/javascripts/admin/templates/components/themes-list-item.hbs @@ -1,4 +1,4 @@ -{{#link-to 'adminCustomizeThemes.show' theme replace=true}} +
{{plugin-outlet name="admin-customize-themes-list-item" connectorTagName='span' args=(hash theme=theme)}}
@@ -7,28 +7,38 @@ - {{#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}}
- {{#if hasComponents}} + {{#if displayComponents}}
{{#each children as |child|}} {{child}} {{/each}} - {{#if hasMore}} - {{I18n "admin.customize.theme.and_x_more" count=moreCount}} + {{#if displayHasMore}} + + {{#if childrenExpanded}} + {{I18n "admin.customize.theme.collapse"}} + {{else}} + {{I18n "admin.customize.theme.and_x_more" count=moreCount}} + {{/if}} + {{/if}}
{{/if}} -{{/link-to}} +
diff --git a/app/assets/javascripts/admin/templates/components/themes-list.hbs b/app/assets/javascripts/admin/templates/components/themes-list.hbs index 53a404b0fe..5daf60e8a9 100644 --- a/app/assets/javascripts/admin/templates/components/themes-list.hbs +++ b/app/assets/javascripts/admin/templates/components/themes-list.hbs @@ -7,16 +7,16 @@ -
+
{{#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}} diff --git a/app/assets/javascripts/admin/templates/customize-themes-index.hbs b/app/assets/javascripts/admin/templates/customize-themes-index.hbs new file mode 100644 index 0000000000..307044d16e --- /dev/null +++ b/app/assets/javascripts/admin/templates/customize-themes-index.hbs @@ -0,0 +1,11 @@ +
+

{{I18n "admin.customize.theme.themes_intro"}}

+
+ {{#each externalResources as |resource|}} + + {{d-icon resource.icon}} + {{I18n resource.key}} + + {{/each}} +
+
diff --git a/app/assets/javascripts/admin/templates/customize-themes-show.hbs b/app/assets/javascripts/admin/templates/customize-themes-show.hbs index 1d490ade5e..9cb796793a 100644 --- a/app/assets/javascripts/admin/templates/customize-themes-show.hbs +++ b/app/assets/javascripts/admin/templates/customize-themes-show.hbs @@ -52,11 +52,11 @@
{{i18n "admin.customize.theme.css_html"}}
- {{#if hasEditedFields}} + {{#if model.hasEditedFields}}
{{i18n "admin.customize.theme.custom_sections"}}
    - {{#each editedDescriptions as |desc|}} -
  • {{desc}}
  • + {{#each editedFieldsFormatted as |field|}} +
  • {{field}}
  • {{/each}}
{{else}} @@ -127,24 +127,17 @@ {{#if availableChildThemes}}
{{i18n "admin.customize.theme.theme_components"}}
- {{#unless model.childThemes.length}} -
- -
- {{else}} -
    - {{#each model.childThemes as |child|}} -
  • {{#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"}}
  • - {{/each}} -
- {{/unless}} + {{#if model.childThemes.length}} +
    + {{#each model.childThemes as |child|}} +
  • {{#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"}}
  • + {{/each}} +
+ {{/if}} {{#if selectableChildThemes}}
- {{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}}
{{/if}}
diff --git a/app/assets/stylesheets/common/admin/customize.scss b/app/assets/stylesheets/common/admin/customize.scss index ccd02cedf6..beefd8de9a 100644 --- a/app/assets/stylesheets/common/admin/customize.scss +++ b/app/assets/stylesheets/common/admin/customize.scss @@ -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%; diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index df1eb640bd..c9508a605b 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -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" diff --git a/test/javascripts/admin/components/themes-list-item-test.js.es6 b/test/javascripts/admin/components/themes-list-item-test.js.es6 index da7eda3c6b..c08f1805b5 100644 --- a/test/javascripts/admin/components/themes-list-item-test.js.es6 +++ b/test/javascripts/admin/components/themes-list-item-test.js.es6 @@ -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 }) ); },