FEATURE: Allow themes to override color transformation variables (#7987)
Theme developers can now add any of the transformed color variables to their color scheme in about.json. For example ``` "color_schemes": { "Light": { "primary": "333333", "secondary": "ffffff", "primary-low": "ff0000" } }, ``` would override the primary-low variable when compiling SCSS for the color scheme. The primary-low variable will also be visible in administrator color palette UI.
This commit is contained in:
parent
750802bf56
commit
d348368ab6
|
@ -3,7 +3,7 @@ import {
|
|||
observes,
|
||||
on
|
||||
} from "ember-addons/ember-computed-decorators";
|
||||
import { propertyNotEqual, i18n } from "discourse/lib/computed";
|
||||
import { propertyNotEqual } from "discourse/lib/computed";
|
||||
|
||||
const ColorSchemeColor = Discourse.Model.extend({
|
||||
@on("init")
|
||||
|
@ -42,9 +42,23 @@ const ColorSchemeColor = Discourse.Model.extend({
|
|||
}
|
||||
},
|
||||
|
||||
translatedName: i18n("name", "admin.customize.colors.%@.name"),
|
||||
@computed("name")
|
||||
translatedName(name) {
|
||||
if (!this.is_advanced) {
|
||||
return I18n.t(`admin.customize.colors.${name}.name`);
|
||||
} else {
|
||||
return name;
|
||||
}
|
||||
},
|
||||
|
||||
description: i18n("name", "admin.customize.colors.%@.description"),
|
||||
@computed("name")
|
||||
description(name) {
|
||||
if (!this.is_advanced) {
|
||||
return I18n.t(`admin.customize.colors.${name}.description`);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
brightness returns a number between 0 (darkest) to 255 (brightest).
|
||||
|
|
|
@ -128,7 +128,8 @@ ColorScheme.reopenClass({
|
|||
return ColorSchemeColor.create({
|
||||
name: c.name,
|
||||
hex: c.hex,
|
||||
default_hex: c.default_hex
|
||||
default_hex: c.default_hex,
|
||||
is_advanced: c.is_advanced
|
||||
});
|
||||
})
|
||||
})
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
// standard color transformations, use these if possible, and add any new dark-light-diffs here
|
||||
// any variables defined here can be added in theme color schemes
|
||||
// all variables should have the !default flag
|
||||
|
||||
//primary
|
||||
$primary-very-low: dark-light-diff($primary, $secondary, 97%, -82%) !default;
|
||||
$primary-low: dark-light-diff($primary, $secondary, 90%, -78%) !default;
|
||||
$primary-low-mid: dark-light-diff($primary, $secondary, 70%, -45%) !default;
|
||||
$primary-medium: dark-light-diff($primary, $secondary, 50%, -35%) !default;
|
||||
$primary-high: dark-light-diff($primary, $secondary, 30%, -25%) !default;
|
||||
$primary-very-high: dark-light-diff($primary, $secondary, 15%, -10%) !default;
|
||||
|
||||
//header_primary
|
||||
$header_primary-low: dark-light-diff(
|
||||
$header_primary,
|
||||
$header_background,
|
||||
90%,
|
||||
-78%
|
||||
) !default;
|
||||
$header_primary-low-mid: dark-light-diff(
|
||||
$header_primary,
|
||||
$header_background,
|
||||
70%,
|
||||
-45%
|
||||
) !default;
|
||||
|
||||
$header_primary-medium: dark-light-diff(
|
||||
$header_primary,
|
||||
$header_background,
|
||||
50%,
|
||||
-35%
|
||||
) !default;
|
||||
$header_primary-high: dark-light-diff(
|
||||
$header_primary,
|
||||
$header_background,
|
||||
30%,
|
||||
-25%
|
||||
) !default;
|
||||
$header_primary-very-high: dark-light-diff(
|
||||
$header_primary,
|
||||
$header_background,
|
||||
15%,
|
||||
-10%
|
||||
) !default;
|
||||
|
||||
//secondary
|
||||
$secondary-low: dark-light-diff($secondary, $primary, 70%, -70%) !default;
|
||||
$secondary-medium: dark-light-diff($secondary, $primary, 50%, -50%) !default;
|
||||
$secondary-high: dark-light-diff($secondary, $primary, 30%, -35%) !default;
|
||||
$secondary-very-high: dark-light-diff($secondary, $primary, 7%, -7%) !default;
|
||||
|
||||
//tertiary
|
||||
$tertiary-low: dark-light-diff($tertiary, $secondary, 85%, -65%) !default;
|
||||
$tertiary-medium: dark-light-diff($tertiary, $secondary, 50%, -45%) !default;
|
||||
$tertiary-high: dark-light-diff($tertiary, $secondary, 20%, -25%) !default;
|
||||
|
||||
//quaternary
|
||||
$quaternary-low: dark-light-diff($quaternary, $secondary, 70%, -70%) !default;
|
||||
|
||||
//highlight
|
||||
$highlight-low: dark-light-diff($highlight, $secondary, 70%, -80%) !default;
|
||||
$highlight-medium: dark-light-diff($highlight, $secondary, 50%, -55%) !default;
|
||||
$highlight-high: dark-light-diff($highlight, $secondary, -50%, -10%) !default;
|
||||
|
||||
//danger
|
||||
$danger-low: dark-light-diff($danger, $secondary, 85%, -64%) !default;
|
||||
$danger-medium: dark-light-diff($danger, $secondary, 30%, -35%) !default;
|
||||
|
||||
//success
|
||||
$success-low: dark-light-diff($success, $secondary, 80%, -60%) !default;
|
||||
$success-medium: dark-light-diff($success, $secondary, 50%, -40%) !default;
|
||||
|
||||
//love
|
||||
$love-low: dark-light-diff($love, $secondary, 85%, -60%) !default;
|
||||
|
||||
//wiki
|
||||
$wiki: green !default;
|
|
@ -204,78 +204,4 @@ $box-shadow: (
|
|||
}
|
||||
}
|
||||
|
||||
// standard color transformations, use these if possible, and add any new dark-light-diffs here
|
||||
|
||||
//primary
|
||||
$primary-very-low: dark-light-diff($primary, $secondary, 97%, -82%);
|
||||
$primary-low: dark-light-diff($primary, $secondary, 90%, -78%);
|
||||
$primary-low-mid: dark-light-diff($primary, $secondary, 70%, -45%);
|
||||
$primary-medium: dark-light-diff($primary, $secondary, 50%, -35%);
|
||||
$primary-high: dark-light-diff($primary, $secondary, 30%, -25%);
|
||||
$primary-very-high: dark-light-diff($primary, $secondary, 15%, -10%);
|
||||
|
||||
//header_primary
|
||||
$header_primary-low: dark-light-diff(
|
||||
$header_primary,
|
||||
$header_background,
|
||||
90%,
|
||||
-78%
|
||||
);
|
||||
$header_primary-low-mid: dark-light-diff(
|
||||
$header_primary,
|
||||
$header_background,
|
||||
70%,
|
||||
-45%
|
||||
);
|
||||
|
||||
$header_primary-medium: dark-light-diff(
|
||||
$header_primary,
|
||||
$header_background,
|
||||
50%,
|
||||
-35%
|
||||
);
|
||||
$header_primary-high: dark-light-diff(
|
||||
$header_primary,
|
||||
$header_background,
|
||||
30%,
|
||||
-25%
|
||||
);
|
||||
$header_primary-very-high: dark-light-diff(
|
||||
$header_primary,
|
||||
$header_background,
|
||||
15%,
|
||||
-10%
|
||||
);
|
||||
|
||||
//secondary
|
||||
$secondary-low: dark-light-diff($secondary, $primary, 70%, -70%);
|
||||
$secondary-medium: dark-light-diff($secondary, $primary, 50%, -50%);
|
||||
$secondary-high: dark-light-diff($secondary, $primary, 30%, -35%);
|
||||
$secondary-very-high: dark-light-diff($secondary, $primary, 7%, -7%);
|
||||
|
||||
//tertiary
|
||||
$tertiary-low: dark-light-diff($tertiary, $secondary, 85%, -65%);
|
||||
$tertiary-medium: dark-light-diff($tertiary, $secondary, 50%, -45%);
|
||||
$tertiary-high: dark-light-diff($tertiary, $secondary, 20%, -25%);
|
||||
|
||||
//quaternary
|
||||
$quaternary-low: dark-light-diff($quaternary, $secondary, 70%, -70%);
|
||||
|
||||
//highlight
|
||||
$highlight-low: dark-light-diff($highlight, $secondary, 70%, -80%);
|
||||
$highlight-medium: dark-light-diff($highlight, $secondary, 50%, -55%);
|
||||
$highlight-high: dark-light-diff($highlight, $secondary, -50%, -10%);
|
||||
|
||||
//danger
|
||||
$danger-low: dark-light-diff($danger, $secondary, 85%, -64%);
|
||||
$danger-medium: dark-light-diff($danger, $secondary, 30%, -35%);
|
||||
|
||||
//success
|
||||
$success-low: dark-light-diff($success, $secondary, 80%, -60%);
|
||||
$success-medium: dark-light-diff($success, $secondary, 50%, -40%);
|
||||
|
||||
//love
|
||||
$love-low: dark-light-diff($love, $secondary, 85%, -60%);
|
||||
|
||||
//wiki
|
||||
$wiki: green;
|
||||
@import "color_transformations";
|
||||
|
|
|
@ -140,6 +140,7 @@ class ColorScheme < ActiveRecord::Base
|
|||
validates_associated :color_scheme_colors
|
||||
|
||||
BASE_COLORS_FILE = "#{Rails.root}/app/assets/stylesheets/common/foundation/colors.scss"
|
||||
COLOR_TRANSFORMATION_FILE = "#{Rails.root}/app/assets/stylesheets/common/foundation/color_transformations.scss"
|
||||
|
||||
@mutex = Mutex.new
|
||||
|
||||
|
@ -157,6 +158,20 @@ class ColorScheme < ActiveRecord::Base
|
|||
@base_colors
|
||||
end
|
||||
|
||||
def self.color_transformation_variables
|
||||
return @transformation_variables if @transformation_variables
|
||||
@mutex.synchronize do
|
||||
return @transformation_variables if @transformation_variables
|
||||
transformation_variables = []
|
||||
File.readlines(COLOR_TRANSFORMATION_FILE).each do |line|
|
||||
matches = /\$([\w\-_]+):.*/.match(line.strip)
|
||||
transformation_variables.append(matches[1]) if matches
|
||||
end
|
||||
@transformation_variables = transformation_variables
|
||||
end
|
||||
@transformation_variables
|
||||
end
|
||||
|
||||
def self.base_color_schemes
|
||||
base_color_scheme_colors.map do |hash|
|
||||
scheme = new(name: I18n.t("color_schemes.#{hash[:id].downcase.gsub(' ', '_')}"), base_scheme_id: hash[:id])
|
||||
|
|
|
@ -203,25 +203,33 @@ class RemoteTheme < ActiveRecord::Base
|
|||
|
||||
schemes&.each do |name, colors|
|
||||
missing_scheme_names.delete(name)
|
||||
existing = theme.color_schemes.find_by(name: name)
|
||||
if existing
|
||||
existing.colors.each do |c|
|
||||
override = normalize_override(colors[c.name])
|
||||
if override && c.hex != override
|
||||
c.hex = override
|
||||
theme.notify_color_change(c)
|
||||
end
|
||||
end
|
||||
ordered_schemes << existing
|
||||
else
|
||||
scheme = theme.color_schemes.build(name: name)
|
||||
scheme = theme.color_schemes.find_by(name: name) || theme.color_schemes.build(name: name)
|
||||
|
||||
# Update main colors
|
||||
ColorScheme.base.colors_hashes.each do |color|
|
||||
override = normalize_override(colors[color[:name]])
|
||||
scheme.color_scheme_colors << ColorSchemeColor.new(name: color[:name], hex: override || color[:hex])
|
||||
color_scheme_color = scheme.color_scheme_colors.to_a.find { |c| c.name == color[:name] } ||
|
||||
scheme.color_scheme_colors.build(name: color[:name])
|
||||
color_scheme_color.hex = override || color[:hex]
|
||||
theme.notify_color_change(color_scheme_color)
|
||||
end
|
||||
|
||||
# Update advanced colors
|
||||
ColorScheme.color_transformation_variables.each do |variable_name|
|
||||
override = normalize_override(colors[variable_name])
|
||||
color_scheme_color = scheme.color_scheme_colors.to_a.find { |c| c.name == variable_name }
|
||||
if override
|
||||
color_scheme_color ||= scheme.color_scheme_colors.build(name: variable_name)
|
||||
color_scheme_color.hex = override
|
||||
theme.notify_color_change(color_scheme_color)
|
||||
elsif color_scheme_color # No longer specified in about.json, delete record
|
||||
scheme.color_scheme_colors.delete(color_scheme_color)
|
||||
theme.notify_color_change(nil, scheme: scheme)
|
||||
end
|
||||
end
|
||||
|
||||
ordered_schemes << scheme
|
||||
end
|
||||
end
|
||||
|
||||
if missing_scheme_names.length > 0
|
||||
ColorScheme.where(id: missing_scheme_names.values).delete_all
|
||||
|
|
|
@ -34,20 +34,18 @@ class Theme < ActiveRecord::Base
|
|||
where('user_selectable OR id = ?', SiteSetting.default_theme_id)
|
||||
}
|
||||
|
||||
def notify_color_change(color)
|
||||
changed_colors << color
|
||||
def notify_color_change(color, scheme: nil)
|
||||
scheme ||= color.color_scheme
|
||||
changed_colors << color if color
|
||||
changed_schemes << scheme if scheme
|
||||
end
|
||||
|
||||
after_save do
|
||||
color_schemes = {}
|
||||
changed_colors.each do |color|
|
||||
color.save!
|
||||
color_schemes[color.color_scheme_id] ||= color.color_scheme
|
||||
end
|
||||
|
||||
color_schemes.values.each(&:save!)
|
||||
changed_colors.each(&:save!)
|
||||
changed_schemes.each(&:save!)
|
||||
|
||||
changed_colors.clear
|
||||
changed_schemes.clear
|
||||
|
||||
changed_fields.each(&:save!)
|
||||
changed_fields.clear
|
||||
|
@ -343,6 +341,10 @@ class Theme < ActiveRecord::Base
|
|||
@changed_colors ||= []
|
||||
end
|
||||
|
||||
def changed_schemes
|
||||
@changed_schemes ||= Set.new
|
||||
end
|
||||
|
||||
def set_field(target:, name:, value: nil, type: nil, type_id: nil, upload_id: nil)
|
||||
name = name.to_s
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ColorSchemeColorSerializer < ApplicationSerializer
|
||||
attributes :name, :hex, :default_hex
|
||||
attributes :name, :hex, :default_hex, :is_advanced
|
||||
|
||||
def hex
|
||||
object.hex # otherwise something crazy is returned
|
||||
|
@ -15,4 +15,8 @@ class ColorSchemeColorSerializer < ApplicationSerializer
|
|||
object.hex
|
||||
end
|
||||
end
|
||||
|
||||
def is_advanced
|
||||
!ColorScheme.base_colors.keys.include?(object.name)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,7 +20,7 @@ describe RemoteTheme do
|
|||
repo_dir
|
||||
end
|
||||
|
||||
def about_json(love_color: "FAFAFA", color_scheme_name: "Amazing", about_url: "https://www.site.com/about")
|
||||
def about_json(love_color: "FAFAFA", tertiary_low_color: "FFFFFF", color_scheme_name: "Amazing", about_url: "https://www.site.com/about")
|
||||
<<~JSON
|
||||
{
|
||||
"name": "awesome theme",
|
||||
|
@ -33,7 +33,8 @@ describe RemoteTheme do
|
|||
},
|
||||
"color_schemes": {
|
||||
"#{color_scheme_name}": {
|
||||
"love": "#{love_color}"
|
||||
"love": "#{love_color}",
|
||||
"tertiary-low": "#{tertiary_low_color}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -105,6 +106,7 @@ describe RemoteTheme do
|
|||
scheme = ColorScheme.find_by(theme_id: @theme.id)
|
||||
expect(scheme.name).to eq("Amazing")
|
||||
expect(scheme.colors.find_by(name: 'love').hex).to eq('fafafa')
|
||||
expect(scheme.colors.find_by(name: 'tertiary-low').hex).to eq('ffffff')
|
||||
|
||||
expect(@theme.color_scheme_id).to eq(scheme.id)
|
||||
@theme.update(color_scheme_id: nil)
|
||||
|
@ -150,7 +152,7 @@ describe RemoteTheme do
|
|||
expect(remote.about_url).to eq("https://newsite.com/about")
|
||||
|
||||
# It should be able to remove old colors as well
|
||||
File.write("#{initial_repo}/about.json", about_json(love_color: "BABABA", color_scheme_name: "Amazing 2"))
|
||||
File.write("#{initial_repo}/about.json", about_json(love_color: "BABABA", tertiary_low_color: "", color_scheme_name: "Amazing 2"))
|
||||
`cd #{initial_repo} && git commit -am "update"`
|
||||
|
||||
remote.update_from_remote
|
||||
|
@ -160,6 +162,9 @@ describe RemoteTheme do
|
|||
scheme_count = ColorScheme.where(theme_id: @theme.id).count
|
||||
expect(scheme_count).to eq(1)
|
||||
|
||||
scheme = ColorScheme.find_by(theme_id: @theme.id)
|
||||
expect(scheme.colors.find_by(name: 'tertiary_low_color')).to eq(nil)
|
||||
|
||||
# It should detect local changes
|
||||
@theme.set_field(target: :common, name: :scss, value: 'body {background-color: blue};')
|
||||
@theme.save
|
||||
|
|
Loading…
Reference in New Issue