mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-27 09:36:19 -05:00
implement color picking from predefined set for category badges + option to change foreground color
This commit is contained in:
parent
a8c44d90a3
commit
8784c55188
17 changed files with 142 additions and 35 deletions
|
@ -39,10 +39,11 @@ Discourse.Utilities = {
|
||||||
|
|
||||||
// Create a badge like category link
|
// Create a badge like category link
|
||||||
categoryLink: function(category) {
|
categoryLink: function(category) {
|
||||||
var color, name, description, result;
|
var color, textColor, name, description, result;
|
||||||
if (!category) return "";
|
if (!category) return "";
|
||||||
|
|
||||||
color = Em.get(category, 'color');
|
color = Em.get(category, 'color');
|
||||||
|
textColor = Em.get(category, 'text_color');
|
||||||
name = Em.get(category, 'name');
|
name = Em.get(category, 'name');
|
||||||
description = Em.get(category, 'description');
|
description = Em.get(category, 'description');
|
||||||
|
|
||||||
|
@ -52,7 +53,7 @@ Discourse.Utilities = {
|
||||||
// Add description if we have it
|
// Add description if we have it
|
||||||
if (description) result += "title=\"" + description + "\" ";
|
if (description) result += "title=\"" + description + "\" ";
|
||||||
|
|
||||||
return result + "style=\"background-color: #" + color + "\">" + name + "</a>";
|
return result + "style=\"background-color: #" + color + "; color: #" + textColor + ";\">" + name + "</a>";
|
||||||
},
|
},
|
||||||
|
|
||||||
avatarUrl: function(username, size, template) {
|
avatarUrl: function(username, size, template) {
|
||||||
|
|
|
@ -13,8 +13,8 @@ Discourse.Category = Discourse.Model.extend({
|
||||||
}).property('name'),
|
}).property('name'),
|
||||||
|
|
||||||
style: (function() {
|
style: (function() {
|
||||||
return "background-color: #" + (this.get('color'));
|
return "background-color: #" + (this.get('category.color')) + "; color: #" + (this.get('category.text_color')) + ";";
|
||||||
}).property('color'),
|
}).property('color', 'text_color'),
|
||||||
|
|
||||||
moreTopics: (function() {
|
moreTopics: (function() {
|
||||||
return this.get('topic_count') > Discourse.SiteSettings.category_featured_topics;
|
return this.get('topic_count') > Discourse.SiteSettings.category_featured_topics;
|
||||||
|
@ -32,7 +32,8 @@ Discourse.Category = Discourse.Model.extend({
|
||||||
return this.ajax(url, {
|
return this.ajax(url, {
|
||||||
data: {
|
data: {
|
||||||
name: this.get('name'),
|
name: this.get('name'),
|
||||||
color: this.get('color')
|
color: this.get('color'),
|
||||||
|
text_color: this.get('text_color')
|
||||||
},
|
},
|
||||||
type: this.get('id') ? 'PUT' : 'POST',
|
type: this.get('id') ? 'PUT' : 'POST',
|
||||||
success: function(result) { return args.success(result); },
|
success: function(result) { return args.success(result); },
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<div class='contents'>
|
<div class='contents'>
|
||||||
<span class='badge-category' style='background-color: #{{unbound view.color}}'>{{unbound view.name}}</span>
|
<span class='badge-category' style='background-color: #{{unbound view.color}}; color: #{{unbound view.text_color}}'>{{unbound view.name}}</span>
|
||||||
|
|
||||||
{{#if view.excerpt}}
|
{{#if view.excerpt}}
|
||||||
<div class='description'>
|
<div class='description'>
|
||||||
|
|
|
@ -20,11 +20,22 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label>{{i18n category.color}}</label>
|
<label>{{i18n category.badge_colors}}</label>
|
||||||
|
|
||||||
<div class='input-prepend input-append'>
|
<div class="category-color-editor">
|
||||||
<span class='add-on'>#</span>{{view Discourse.TextField valueBinding="view.category.color" placeholderKey="category.color_placeholder" maxlength="6"}}
|
<span class='badge-category' {{bindAttr style="view.colorStyle"}}>{{i18n preview}}</span>
|
||||||
<span class='badge-category' {{bindAttr style="view.colorStyle"}}>{{i18n preview}}</span>
|
|
||||||
|
<div class='input-prepend input-append' style="margin-top: 10px;">
|
||||||
|
<span class='color-title'>{{i18n category.background_color}}:</span>
|
||||||
|
<span class='add-on'>#</span>{{view Discourse.TextField valueBinding="view.category.color" placeholderKey="category.color_placeholder" maxlength="6"}}
|
||||||
|
{{view Discourse.ColorsView colorsBinding="view.predefinedColors" valueBinding="view.category.color"}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='input-prepend input-append'>
|
||||||
|
<span class='color-title'>{{i18n category.foreground_color}}:</span>
|
||||||
|
<span class='add-on'>#</span>{{view Discourse.TextField valueBinding="view.category.text_color" placeholderKey="category.color_placeholder" maxlength="6"}}
|
||||||
|
{{view Discourse.ColorsView colorsBinding="view.predefinedColors" valueBinding="view.category.text_color"}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{#with view.content}}
|
{{#with view.content}}
|
||||||
<a href='{{unbound url}}'>
|
<a href='{{unbound url}}'>
|
||||||
<span class='badge-category' style="background-color: #{{unbound color}}">{{unbound title}}</span>
|
<span class='badge-category' style="background-color: #{{unbound color}}; color: #{{unbound text_color}};">{{unbound title}}</span>
|
||||||
</a>
|
</a>
|
||||||
{{/with}}
|
{{/with}}
|
||||||
|
|
||||||
|
|
|
@ -8,12 +8,13 @@
|
||||||
**/
|
**/
|
||||||
Discourse.ComboboxViewCategory = Discourse.ComboboxView.extend({
|
Discourse.ComboboxViewCategory = Discourse.ComboboxView.extend({
|
||||||
none: 'category.none',
|
none: 'category.none',
|
||||||
dataAttributes: ['color', 'description'],
|
dataAttributes: ['color', 'text_color', 'description'],
|
||||||
|
|
||||||
template: function(text, templateData) {
|
template: function(text, templateData) {
|
||||||
if (!templateData.color) return text;
|
if (!templateData.color) return text;
|
||||||
|
|
||||||
var result = "<span class='badge-category' style='background-color: #" + templateData.color + "' "
|
var result = "<span class='badge-category' style='background-color: #" + templateData.color + '; color: #' +
|
||||||
|
templateData.text_color + ";' ";
|
||||||
if (templateData.description && templateData.description !== 'null') {
|
if (templateData.description && templateData.description !== 'null') {
|
||||||
result += "title=\"" + templateData.description + "\" ";
|
result += "title=\"" + templateData.description + "\" ";
|
||||||
}
|
}
|
||||||
|
|
34
app/assets/javascripts/discourse/views/modal/colors_view.js
Normal file
34
app/assets/javascripts/discourse/views/modal/colors_view.js
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/**
|
||||||
|
This view shows an array of buttons for selection of a color from a predefined set.
|
||||||
|
|
||||||
|
@class ColorsView
|
||||||
|
@extends Ember.ContainerView
|
||||||
|
@namespace Discourse
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.ColorsView = Ember.ContainerView.extend({
|
||||||
|
classNames: 'colors-container',
|
||||||
|
|
||||||
|
init: function() {
|
||||||
|
this._super();
|
||||||
|
return this.createButtons();
|
||||||
|
},
|
||||||
|
|
||||||
|
createButtons: function() {
|
||||||
|
var colors = this.get('colors');
|
||||||
|
var _this = this;
|
||||||
|
|
||||||
|
colors.each(function(color) {
|
||||||
|
_this.addObject(Discourse.View.create({
|
||||||
|
tagName: 'button',
|
||||||
|
attributeBindings: ['style'],
|
||||||
|
classNames: ['colorpicker'],
|
||||||
|
style: 'background-color: #' + color + ';',
|
||||||
|
click: function() {
|
||||||
|
_this.set("value", color);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
|
@ -18,8 +18,11 @@ Discourse.EditCategoryView = Discourse.ModalBodyView.extend({
|
||||||
}).property('category.name', 'category.color'),
|
}).property('category.name', 'category.color'),
|
||||||
|
|
||||||
colorStyle: (function() {
|
colorStyle: (function() {
|
||||||
return "background-color: #" + (this.get('category.color')) + ";";
|
return "background-color: #" + (this.get('category.color')) + "; color: #" + (this.get('category.text_color')) + ";";
|
||||||
}).property('category.color'),
|
}).property('category.color', 'category.text_color'),
|
||||||
|
|
||||||
|
predefinedColors: ["FFFFFF", "000000", "AECFC6", "836953", "77DD77", "FFB347", "FDFD96", "536878",
|
||||||
|
"EC5800", "0096E0", "7C4848", "9AC932", "BA160C", "003366", "B19CD9", "E4717A"],
|
||||||
|
|
||||||
title: (function() {
|
title: (function() {
|
||||||
if (this.get('category.id')) return Em.String.i18n("category.edit_long");
|
if (this.get('category.id')) return Em.String.i18n("category.edit_long");
|
||||||
|
@ -36,7 +39,7 @@ Discourse.EditCategoryView = Discourse.ModalBodyView.extend({
|
||||||
if (this.get('category')) {
|
if (this.get('category')) {
|
||||||
this.set('id', this.get('category.slug'));
|
this.set('id', this.get('category.slug'));
|
||||||
} else {
|
} else {
|
||||||
this.set('category', Discourse.Category.create({ color: 'AB9364' }));
|
this.set('category', Discourse.Category.create({ color: 'AB9364', text_color: 'FFFFFF' }));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
29
app/assets/stylesheets/application/colorpicker.css.scss
Normal file
29
app/assets/stylesheets/application/colorpicker.css.scss
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// styles for the category badge color picker
|
||||||
|
|
||||||
|
@import "foundation/variables";
|
||||||
|
@import "foundation/mixins";
|
||||||
|
|
||||||
|
.category-color-editor {
|
||||||
|
input {
|
||||||
|
width: 70px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-title {
|
||||||
|
display: inline-block;
|
||||||
|
width: 130px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.colors-container {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: bottom;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
padding-left: 15px;
|
||||||
|
|
||||||
|
.colorpicker {
|
||||||
|
border: 1px solid $darkish_gray;
|
||||||
|
width: 15px;
|
||||||
|
height: 15px;
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,7 +43,7 @@ class CategoriesController < ApplicationController
|
||||||
private
|
private
|
||||||
|
|
||||||
def category_param_keys
|
def category_param_keys
|
||||||
[:name, :color]
|
[:name, :color, :text_color]
|
||||||
end
|
end
|
||||||
|
|
||||||
def category_params
|
def category_params
|
||||||
|
|
|
@ -3,7 +3,7 @@ require_dependency 'excerpt_type'
|
||||||
class CategoryExcerptSerializer < ActiveModel::Serializer
|
class CategoryExcerptSerializer < ActiveModel::Serializer
|
||||||
include ExcerptType
|
include ExcerptType
|
||||||
|
|
||||||
attributes :excerpt, :name, :color, :slug, :topic_url, :topics_year,
|
attributes :excerpt, :name, :color, :text_color, :slug, :topic_url, :topics_year,
|
||||||
:topics_month, :topics_week, :category_url, :can_edit, :can_delete
|
:topics_month, :topics_week, :category_url, :can_edit, :can_delete
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ class CategorySerializer < ApplicationSerializer
|
||||||
attributes :id,
|
attributes :id,
|
||||||
:name,
|
:name,
|
||||||
:color,
|
:color,
|
||||||
|
:text_color,
|
||||||
:slug,
|
:slug,
|
||||||
:topic_count,
|
:topic_count,
|
||||||
:description,
|
:description,
|
||||||
|
|
|
@ -584,7 +584,9 @@ cs:
|
||||||
name: "Název kategorie"
|
name: "Název kategorie"
|
||||||
description: "Popis"
|
description: "Popis"
|
||||||
topic: "téma kategorie"
|
topic: "téma kategorie"
|
||||||
color: "Barva"
|
badge_colors: "Barvy štítku"
|
||||||
|
background_color: "Barva pozadí"
|
||||||
|
foreground_color: "Barva textu"
|
||||||
name_placeholder: "Měl by být krátký a výstižný."
|
name_placeholder: "Měl by být krátký a výstižný."
|
||||||
color_placeholder: "Jakákoliv webová barva"
|
color_placeholder: "Jakákoliv webová barva"
|
||||||
delete_confirm: "Opravdu chcete odstranit tuto kategorii?"
|
delete_confirm: "Opravdu chcete odstranit tuto kategorii?"
|
||||||
|
|
|
@ -587,7 +587,9 @@ en:
|
||||||
name: "Category Name"
|
name: "Category Name"
|
||||||
description: "Description"
|
description: "Description"
|
||||||
topic: "category topic"
|
topic: "category topic"
|
||||||
color: "Color"
|
badge_colors: "Badge colors"
|
||||||
|
background_color: "Background color"
|
||||||
|
foreground_color: "Foreground color"
|
||||||
name_placeholder: "Should be short and succinct."
|
name_placeholder: "Should be short and succinct."
|
||||||
color_placeholder: "Any web color"
|
color_placeholder: "Any web color"
|
||||||
delete_confirm: "Are you sure you want to delete that category?"
|
delete_confirm: "Are you sure you want to delete that category?"
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
class AddForegroundColorToCategories < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
add_column :categories, :text_color, :string, limit: 6, null: false, default: 'FFFFFF'
|
||||||
|
end
|
||||||
|
end
|
|
@ -14,7 +14,8 @@ module Search
|
||||||
'/users/' || u.username_lower AS url,
|
'/users/' || u.username_lower AS url,
|
||||||
u.username AS title,
|
u.username AS title,
|
||||||
u.email,
|
u.email,
|
||||||
NULL AS color
|
NULL AS color,
|
||||||
|
NULL AS text_color
|
||||||
FROM users AS u
|
FROM users AS u
|
||||||
JOIN users_search s on s.id = u.id
|
JOIN users_search s on s.id = u.id
|
||||||
WHERE s.search_data @@ TO_TSQUERY(:locale, :query)
|
WHERE s.search_data @@ TO_TSQUERY(:locale, :query)
|
||||||
|
@ -29,7 +30,8 @@ module Search
|
||||||
'/t/slug/' || ft.id AS url,
|
'/t/slug/' || ft.id AS url,
|
||||||
ft.title,
|
ft.title,
|
||||||
NULL AS email,
|
NULL AS email,
|
||||||
NULL AS color
|
NULL AS color,
|
||||||
|
NULL AS text_color
|
||||||
FROM topics AS ft
|
FROM topics AS ft
|
||||||
JOIN posts AS p ON p.topic_id = ft.id AND p.post_number = 1
|
JOIN posts AS p ON p.topic_id = ft.id AND p.post_number = 1
|
||||||
JOIN posts_search s on s.id = p.id
|
JOIN posts_search s on s.id = p.id
|
||||||
|
@ -52,7 +54,8 @@ module Search
|
||||||
'/t/slug/' || ft.id || '/' || p.post_number AS url,
|
'/t/slug/' || ft.id || '/' || p.post_number AS url,
|
||||||
ft.title,
|
ft.title,
|
||||||
NULL AS email,
|
NULL AS email,
|
||||||
NULL AS color
|
NULL AS color,
|
||||||
|
NULL AS text_color
|
||||||
FROM topics AS ft
|
FROM topics AS ft
|
||||||
JOIN posts AS p ON p.topic_id = ft.id AND p.post_number <> 1
|
JOIN posts AS p ON p.topic_id = ft.id AND p.post_number <> 1
|
||||||
JOIN posts_search s on s.id = p.id
|
JOIN posts_search s on s.id = p.id
|
||||||
|
@ -74,7 +77,8 @@ module Search
|
||||||
'/category/' || c.slug AS url,
|
'/category/' || c.slug AS url,
|
||||||
c.name AS title,
|
c.name AS title,
|
||||||
NULL AS email,
|
NULL AS email,
|
||||||
c.color
|
c.color,
|
||||||
|
c.text_color
|
||||||
FROM categories AS c
|
FROM categories AS c
|
||||||
JOIN categories_search s on s.id = c.id
|
JOIN categories_search s on s.id = c.id
|
||||||
WHERE s.search_data @@ TO_TSQUERY(:locale, :query)
|
WHERE s.search_data @@ TO_TSQUERY(:locale, :query)
|
||||||
|
@ -168,6 +172,7 @@ module Search
|
||||||
end
|
end
|
||||||
row.delete('email')
|
row.delete('email')
|
||||||
row.delete('color') unless type == 'category'
|
row.delete('color') unless type == 'category'
|
||||||
|
row.delete('text_color') unless type == 'category'
|
||||||
|
|
||||||
grouped[type] ||= []
|
grouped[type] ||= []
|
||||||
grouped[type] << row
|
grouped[type] << row
|
||||||
|
|
|
@ -14,22 +14,26 @@ describe CategoriesController do
|
||||||
|
|
||||||
it "raises an exception when they don't have permission to create it" do
|
it "raises an exception when they don't have permission to create it" do
|
||||||
Guardian.any_instance.expects(:can_create?).with(Category, nil).returns(false)
|
Guardian.any_instance.expects(:can_create?).with(Category, nil).returns(false)
|
||||||
xhr :post, :create, name: 'hello', color: '#ff0'
|
xhr :post, :create, name: 'hello', color: 'ff0', text_color: 'fff'
|
||||||
response.should be_forbidden
|
response.should be_forbidden
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'raises an exception when the name is missing' do
|
it 'raises an exception when the name is missing' do
|
||||||
lambda { xhr :post, :create, color: '#ff0' }.should raise_error(Discourse::InvalidParameters)
|
lambda { xhr :post, :create, color: 'ff0', text_color: 'fff' }.should raise_error(Discourse::InvalidParameters)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'raises an exception when the color is missing' do
|
it 'raises an exception when the color is missing' do
|
||||||
lambda { xhr :post, :create, name: 'hello' }.should raise_error(Discourse::InvalidParameters)
|
lambda { xhr :post, :create, name: 'hello', text_color: 'fff' }.should raise_error(Discourse::InvalidParameters)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'raises an exception when the text color is missing' do
|
||||||
|
lambda { xhr :post, :create, name: 'hello', color: 'ff0' }.should raise_error(Discourse::InvalidParameters)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'failure' do
|
describe 'failure' do
|
||||||
before do
|
before do
|
||||||
@category = Fabricate(:category, user: @user)
|
@category = Fabricate(:category, user: @user)
|
||||||
xhr :post, :create, name: @category.name, color: '#ff0'
|
xhr :post, :create, name: @category.name, color: 'ff0', text_color: 'fff'
|
||||||
end
|
end
|
||||||
|
|
||||||
it { should_not respond_with(:success) }
|
it { should_not respond_with(:success) }
|
||||||
|
@ -42,7 +46,7 @@ describe CategoriesController do
|
||||||
|
|
||||||
describe 'success' do
|
describe 'success' do
|
||||||
before do
|
before do
|
||||||
xhr :post, :create, name: 'hello', color: '#ff0'
|
xhr :post, :create, name: 'hello', color: 'ff0', text_color: 'fff'
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates a category' do
|
it 'creates a category' do
|
||||||
|
@ -97,22 +101,26 @@ describe CategoriesController do
|
||||||
|
|
||||||
it "raises an exception if they don't have permission to edit it" do
|
it "raises an exception if they don't have permission to edit it" do
|
||||||
Guardian.any_instance.expects(:can_edit?).returns(false)
|
Guardian.any_instance.expects(:can_edit?).returns(false)
|
||||||
xhr :put, :update, id: @category.slug, name: 'hello', color: '#ff0'
|
xhr :put, :update, id: @category.slug, name: 'hello', color: 'ff0', text_color: 'fff'
|
||||||
response.should be_forbidden
|
response.should be_forbidden
|
||||||
end
|
end
|
||||||
|
|
||||||
it "requires a name" do
|
it "requires a name" do
|
||||||
lambda { xhr :put, :update, id: @category.slug, color: '#fff' }.should raise_error(Discourse::InvalidParameters)
|
lambda { xhr :put, :update, id: @category.slug, color: 'fff', text_color: '0ff' }.should raise_error(Discourse::InvalidParameters)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "requires a color" do
|
it "requires a color" do
|
||||||
lambda { xhr :put, :update, id: @category.slug, name: 'asdf'}.should raise_error(Discourse::InvalidParameters)
|
lambda { xhr :put, :update, id: @category.slug, name: 'asdf', text_color: '0ff' }.should raise_error(Discourse::InvalidParameters)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "requires a text color" do
|
||||||
|
lambda { xhr :put, :update, id: @category.slug, name: 'asdf', color: 'fff' }.should raise_error(Discourse::InvalidParameters)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'failure' do
|
describe 'failure' do
|
||||||
before do
|
before do
|
||||||
@other_category = Fabricate(:category, name: 'Other', user: @user )
|
@other_category = Fabricate(:category, name: 'Other', user: @user )
|
||||||
xhr :put, :update, id: @category.id, name: @other_category.name, color: '#ff0'
|
xhr :put, :update, id: @category.id, name: @other_category.name, color: 'ff0', text_color: 'fff'
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns errors on a duplicate category name' do
|
it 'returns errors on a duplicate category name' do
|
||||||
|
@ -126,7 +134,7 @@ describe CategoriesController do
|
||||||
|
|
||||||
describe 'success' do
|
describe 'success' do
|
||||||
before do
|
before do
|
||||||
xhr :put, :update, id: @category.id, name: 'science', color: '#000'
|
xhr :put, :update, id: @category.id, name: 'science', color: '000', text_color: '0ff'
|
||||||
@category.reload
|
@category.reload
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -135,7 +143,11 @@ describe CategoriesController do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'updates the color' do
|
it 'updates the color' do
|
||||||
@category.color.should == '#000'
|
@category.color.should == '000'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates the text color' do
|
||||||
|
@category.text_color.should == '0ff'
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue