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
|
||||
categoryLink: function(category) {
|
||||
var color, name, description, result;
|
||||
var color, textColor, name, description, result;
|
||||
if (!category) return "";
|
||||
|
||||
color = Em.get(category, 'color');
|
||||
textColor = Em.get(category, 'text_color');
|
||||
name = Em.get(category, 'name');
|
||||
description = Em.get(category, 'description');
|
||||
|
||||
|
@ -52,7 +53,7 @@ Discourse.Utilities = {
|
|||
// Add description if we have it
|
||||
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) {
|
||||
|
|
|
@ -13,8 +13,8 @@ Discourse.Category = Discourse.Model.extend({
|
|||
}).property('name'),
|
||||
|
||||
style: (function() {
|
||||
return "background-color: #" + (this.get('color'));
|
||||
}).property('color'),
|
||||
return "background-color: #" + (this.get('category.color')) + "; color: #" + (this.get('category.text_color')) + ";";
|
||||
}).property('color', 'text_color'),
|
||||
|
||||
moreTopics: (function() {
|
||||
return this.get('topic_count') > Discourse.SiteSettings.category_featured_topics;
|
||||
|
@ -32,7 +32,8 @@ Discourse.Category = Discourse.Model.extend({
|
|||
return this.ajax(url, {
|
||||
data: {
|
||||
name: this.get('name'),
|
||||
color: this.get('color')
|
||||
color: this.get('color'),
|
||||
text_color: this.get('text_color')
|
||||
},
|
||||
type: this.get('id') ? 'PUT' : 'POST',
|
||||
success: function(result) { return args.success(result); },
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<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}}
|
||||
<div class='description'>
|
||||
|
|
|
@ -20,11 +20,22 @@
|
|||
|
||||
</div>
|
||||
|
||||
<label>{{i18n category.color}}</label>
|
||||
<label>{{i18n category.badge_colors}}</label>
|
||||
|
||||
<div class='input-prepend input-append'>
|
||||
<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>
|
||||
<div class="category-color-editor">
|
||||
<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>
|
||||
|
||||
</form>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{{#with view.content}}
|
||||
<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>
|
||||
{{/with}}
|
||||
|
||||
|
|
|
@ -8,12 +8,13 @@
|
|||
**/
|
||||
Discourse.ComboboxViewCategory = Discourse.ComboboxView.extend({
|
||||
none: 'category.none',
|
||||
dataAttributes: ['color', 'description'],
|
||||
dataAttributes: ['color', 'text_color', 'description'],
|
||||
|
||||
template: function(text, templateData) {
|
||||
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') {
|
||||
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'),
|
||||
|
||||
colorStyle: (function() {
|
||||
return "background-color: #" + (this.get('category.color')) + ";";
|
||||
}).property('category.color'),
|
||||
return "background-color: #" + (this.get('category.color')) + "; color: #" + (this.get('category.text_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() {
|
||||
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')) {
|
||||
this.set('id', this.get('category.slug'));
|
||||
} 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
|
||||
|
||||
def category_param_keys
|
||||
[:name, :color]
|
||||
[:name, :color, :text_color]
|
||||
end
|
||||
|
||||
def category_params
|
||||
|
|
|
@ -3,7 +3,7 @@ require_dependency 'excerpt_type'
|
|||
class CategoryExcerptSerializer < ActiveModel::Serializer
|
||||
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
|
||||
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ class CategorySerializer < ApplicationSerializer
|
|||
attributes :id,
|
||||
:name,
|
||||
:color,
|
||||
:text_color,
|
||||
:slug,
|
||||
:topic_count,
|
||||
:description,
|
||||
|
|
|
@ -584,7 +584,9 @@ cs:
|
|||
name: "Název kategorie"
|
||||
description: "Popis"
|
||||
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ý."
|
||||
color_placeholder: "Jakákoliv webová barva"
|
||||
delete_confirm: "Opravdu chcete odstranit tuto kategorii?"
|
||||
|
|
|
@ -587,7 +587,9 @@ en:
|
|||
name: "Category Name"
|
||||
description: "Description"
|
||||
topic: "category topic"
|
||||
color: "Color"
|
||||
badge_colors: "Badge colors"
|
||||
background_color: "Background color"
|
||||
foreground_color: "Foreground color"
|
||||
name_placeholder: "Should be short and succinct."
|
||||
color_placeholder: "Any web color"
|
||||
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,
|
||||
u.username AS title,
|
||||
u.email,
|
||||
NULL AS color
|
||||
NULL AS color,
|
||||
NULL AS text_color
|
||||
FROM users AS u
|
||||
JOIN users_search s on s.id = u.id
|
||||
WHERE s.search_data @@ TO_TSQUERY(:locale, :query)
|
||||
|
@ -29,7 +30,8 @@ module Search
|
|||
'/t/slug/' || ft.id AS url,
|
||||
ft.title,
|
||||
NULL AS email,
|
||||
NULL AS color
|
||||
NULL AS color,
|
||||
NULL AS text_color
|
||||
FROM topics AS ft
|
||||
JOIN posts AS p ON p.topic_id = ft.id AND p.post_number = 1
|
||||
JOIN posts_search s on s.id = p.id
|
||||
|
@ -52,7 +54,8 @@ module Search
|
|||
'/t/slug/' || ft.id || '/' || p.post_number AS url,
|
||||
ft.title,
|
||||
NULL AS email,
|
||||
NULL AS color
|
||||
NULL AS color,
|
||||
NULL AS text_color
|
||||
FROM topics AS ft
|
||||
JOIN posts AS p ON p.topic_id = ft.id AND p.post_number <> 1
|
||||
JOIN posts_search s on s.id = p.id
|
||||
|
@ -74,7 +77,8 @@ module Search
|
|||
'/category/' || c.slug AS url,
|
||||
c.name AS title,
|
||||
NULL AS email,
|
||||
c.color
|
||||
c.color,
|
||||
c.text_color
|
||||
FROM categories AS c
|
||||
JOIN categories_search s on s.id = c.id
|
||||
WHERE s.search_data @@ TO_TSQUERY(:locale, :query)
|
||||
|
@ -168,6 +172,7 @@ module Search
|
|||
end
|
||||
row.delete('email')
|
||||
row.delete('color') unless type == 'category'
|
||||
row.delete('text_color') unless type == 'category'
|
||||
|
||||
grouped[type] ||= []
|
||||
grouped[type] << row
|
||||
|
|
|
@ -14,22 +14,26 @@ describe CategoriesController 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)
|
||||
xhr :post, :create, name: 'hello', color: '#ff0'
|
||||
xhr :post, :create, name: 'hello', color: 'ff0', text_color: 'fff'
|
||||
response.should be_forbidden
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
describe 'failure' do
|
||||
before do
|
||||
@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
|
||||
|
||||
it { should_not respond_with(:success) }
|
||||
|
@ -42,7 +46,7 @@ describe CategoriesController do
|
|||
|
||||
describe 'success' do
|
||||
before do
|
||||
xhr :post, :create, name: 'hello', color: '#ff0'
|
||||
xhr :post, :create, name: 'hello', color: 'ff0', text_color: 'fff'
|
||||
end
|
||||
|
||||
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
|
||||
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
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
describe 'failure' do
|
||||
before do
|
||||
@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
|
||||
|
||||
it 'returns errors on a duplicate category name' do
|
||||
|
@ -126,7 +134,7 @@ describe CategoriesController do
|
|||
|
||||
describe 'success' 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
|
||||
end
|
||||
|
||||
|
@ -135,7 +143,11 @@ describe CategoriesController do
|
|||
end
|
||||
|
||||
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
|
||||
|
|
Loading…
Reference in a new issue