DEV: Added support for custom site setting 'emoji_list' (#12414)
Example usage: ``` best_emojis: type: emoji_list default: laughing|open_mouth|cry|angry|hugs client: true ```
This commit is contained in:
parent
105634435f
commit
2308a58113
|
@ -0,0 +1,168 @@
|
||||||
|
import Component from "@ember/component";
|
||||||
|
import I18n from "I18n";
|
||||||
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
|
import { emojiUrlFor } from "discourse/lib/text";
|
||||||
|
import { action, set, setProperties } from "@ember/object";
|
||||||
|
import { later, schedule } from "@ember/runloop";
|
||||||
|
|
||||||
|
export default Component.extend({
|
||||||
|
classNameBindings: [":value-list", ":emoji-list"],
|
||||||
|
values: null,
|
||||||
|
validationMessage: null,
|
||||||
|
emojiPickerIsActive: false,
|
||||||
|
isEditorFocused: false,
|
||||||
|
|
||||||
|
@discourseComputed("values")
|
||||||
|
collection(values) {
|
||||||
|
values = values || "";
|
||||||
|
|
||||||
|
return values
|
||||||
|
.split("|")
|
||||||
|
.filter(Boolean)
|
||||||
|
.map((value) => {
|
||||||
|
return {
|
||||||
|
isEditable: true,
|
||||||
|
isEditing: false,
|
||||||
|
value,
|
||||||
|
emojiUrl: emojiUrlFor(value),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
@action
|
||||||
|
closeEmojiPicker() {
|
||||||
|
this.collection.setEach("isEditing", false);
|
||||||
|
this.set("emojiPickerIsActive", false);
|
||||||
|
this.set("isEditorFocused", false);
|
||||||
|
},
|
||||||
|
|
||||||
|
@action
|
||||||
|
emojiSelected(code) {
|
||||||
|
if (!this._validateInput(code)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const item = this.collection.findBy("isEditing");
|
||||||
|
if (item) {
|
||||||
|
setProperties(item, {
|
||||||
|
value: code,
|
||||||
|
emojiUrl: emojiUrlFor(code),
|
||||||
|
isEditing: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
this._saveValues();
|
||||||
|
} else {
|
||||||
|
const newCollectionValue = {
|
||||||
|
value: code,
|
||||||
|
emojiUrl: emojiUrlFor(code),
|
||||||
|
isEditable: true,
|
||||||
|
isEditing: false,
|
||||||
|
};
|
||||||
|
this.collection.addObject(newCollectionValue);
|
||||||
|
this._saveValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.set("emojiPickerIsActive", false);
|
||||||
|
this.set("isEditorFocused", false);
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("collection")
|
||||||
|
showUpDownButtons(collection) {
|
||||||
|
return collection.length - 1 ? true : false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_splitValues(values) {
|
||||||
|
if (values && values.length) {
|
||||||
|
const emojiList = [];
|
||||||
|
const emojis = values.split("|").filter(Boolean);
|
||||||
|
emojis.forEach((emojiName) => {
|
||||||
|
const emoji = {
|
||||||
|
isEditable: true,
|
||||||
|
isEditing: false,
|
||||||
|
};
|
||||||
|
emoji.value = emojiName;
|
||||||
|
emoji.emojiUrl = emojiUrlFor(emojiName);
|
||||||
|
|
||||||
|
emojiList.push(emoji);
|
||||||
|
});
|
||||||
|
|
||||||
|
return emojiList;
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
@action
|
||||||
|
editValue(index) {
|
||||||
|
this.closeEmojiPicker();
|
||||||
|
schedule("afterRender", () => {
|
||||||
|
if (parseInt(index, 10) >= 0) {
|
||||||
|
const item = this.collection[index];
|
||||||
|
if (item.isEditable) {
|
||||||
|
set(item, "isEditing", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.set("isEditorFocused", true);
|
||||||
|
later(() => {
|
||||||
|
if (this.element && !this.isDestroying && !this.isDestroyed) {
|
||||||
|
this.set("emojiPickerIsActive", true);
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
@action
|
||||||
|
removeValue(value) {
|
||||||
|
this._removeValue(value);
|
||||||
|
},
|
||||||
|
|
||||||
|
@action
|
||||||
|
shift(operation, index) {
|
||||||
|
let futureIndex = index + operation;
|
||||||
|
|
||||||
|
if (futureIndex > this.collection.length - 1) {
|
||||||
|
futureIndex = 0;
|
||||||
|
} else if (futureIndex < 0) {
|
||||||
|
futureIndex = this.collection.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const shiftedEmoji = this.collection[index];
|
||||||
|
this.collection.removeAt(index);
|
||||||
|
this.collection.insertAt(futureIndex, shiftedEmoji);
|
||||||
|
|
||||||
|
this._saveValues();
|
||||||
|
},
|
||||||
|
|
||||||
|
_validateInput(input) {
|
||||||
|
this.set("validationMessage", null);
|
||||||
|
|
||||||
|
if (!emojiUrlFor(input)) {
|
||||||
|
this.set(
|
||||||
|
"validationMessage",
|
||||||
|
I18n.t("admin.site_settings.emoji_list.invalid_input")
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
_removeValue(value) {
|
||||||
|
this.collection.removeObject(value);
|
||||||
|
this._saveValues();
|
||||||
|
},
|
||||||
|
|
||||||
|
_replaceValue(index, newValue) {
|
||||||
|
const item = this.collection[index];
|
||||||
|
if (item.value === newValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
set(item, "value", newValue);
|
||||||
|
this._saveValues();
|
||||||
|
},
|
||||||
|
|
||||||
|
_saveValues() {
|
||||||
|
this.set("values", this.collection.mapBy("value").join("|"));
|
||||||
|
},
|
||||||
|
});
|
|
@ -27,6 +27,7 @@ const CUSTOM_TYPES = [
|
||||||
"tag_list",
|
"tag_list",
|
||||||
"color",
|
"color",
|
||||||
"simple_list",
|
"simple_list",
|
||||||
|
"emoji_list",
|
||||||
];
|
];
|
||||||
|
|
||||||
const AUTO_REFRESH_ON_SAVE = ["logo", "logo_small", "large_icon"];
|
const AUTO_REFRESH_ON_SAVE = ["logo", "logo_small", "large_icon"];
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
{{#if collection}}
|
||||||
|
<ul class="values emoji-value-list">
|
||||||
|
{{#each collection as |data index|}}
|
||||||
|
<li class="value" data-index={{index}}>
|
||||||
|
{{#if data.isEditable}}
|
||||||
|
{{d-button
|
||||||
|
action=(action "removeValue")
|
||||||
|
actionParam=data
|
||||||
|
icon="times"
|
||||||
|
class="remove-value-btn btn-small"
|
||||||
|
}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<div class="value-input emoji-details {{if data.isEditable "can-edit"}} {{if data.isEditing "d-editor-textarea-wrapper"}}" {{on "click" (fn this.editValue index)}} role="button">
|
||||||
|
<img height="15px" width="15px" src={{data.emojiUrl}} class="emoji-list-emoji">
|
||||||
|
<span class="emoji-name">{{data.value}}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#if showUpDownButtons}}
|
||||||
|
{{d-button
|
||||||
|
action=(action "shift" -1 index)
|
||||||
|
icon="arrow-up"
|
||||||
|
class="shift-up-value-btn btn-small"
|
||||||
|
}}
|
||||||
|
{{d-button
|
||||||
|
action=(action "shift" 1 index)
|
||||||
|
icon="arrow-down"
|
||||||
|
class="shift-down-value-btn btn-small"
|
||||||
|
}}
|
||||||
|
{{/if}}
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<div class="value">
|
||||||
|
{{d-button
|
||||||
|
action=(action "editValue")
|
||||||
|
actionParam=data
|
||||||
|
icon="emoji-icon"
|
||||||
|
class="add-emoji-button d-editor-textarea-wrapper"
|
||||||
|
label="admin.site_settings.emoji_list.add_emoji_button.label"
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{emoji-picker
|
||||||
|
isActive=emojiPickerIsActive
|
||||||
|
isEditorFocused=isEditorFocused
|
||||||
|
emojiSelected=(action "emojiSelected")
|
||||||
|
onEmojiPickerClose=(action "closeEmojiPicker")
|
||||||
|
}}
|
||||||
|
|
||||||
|
{{setting-validation-message message=validationMessage}}
|
|
@ -0,0 +1,3 @@
|
||||||
|
{{emoji-value-list setting=setting values=value}}
|
||||||
|
<div class="desc">{{html-safe setting.description}}</div>
|
||||||
|
{{setting-validation-message message=validationMessage}}
|
|
@ -884,6 +884,47 @@ table#user-badges {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.emoji-value-list {
|
||||||
|
margin-left: 0;
|
||||||
|
|
||||||
|
.emoji-details {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 30px;
|
||||||
|
padding: $input-padding;
|
||||||
|
line-height: 1;
|
||||||
|
color: var(--primary);
|
||||||
|
border: 1px solid var(--primary-low);
|
||||||
|
|
||||||
|
.emoji-name {
|
||||||
|
margin-left: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(.can-edit) {
|
||||||
|
pointer-events: none;
|
||||||
|
background-color: var(--primary-very-low);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.value-input {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.value .add-emoji-button {
|
||||||
|
display: block;
|
||||||
|
background-color: var(--primary-low);
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value .add-value-btn,
|
||||||
|
.shift-up-value-btn,
|
||||||
|
.shift-down-value-btn {
|
||||||
|
@include value-btn;
|
||||||
|
margin-right: 0 !important;
|
||||||
|
margin-left: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
.secret-value-list {
|
.secret-value-list {
|
||||||
.value {
|
.value {
|
||||||
flex-flow: row wrap;
|
flex-flow: row wrap;
|
||||||
|
|
|
@ -4,3 +4,6 @@
|
||||||
|
|
||||||
// Import all component-specific files
|
// Import all component-specific files
|
||||||
@import "desktop/components/_index";
|
@import "desktop/components/_index";
|
||||||
|
|
||||||
|
// Import all admin-specific files
|
||||||
|
@import "desktop/admin/_index";
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
@import "admin_base";
|
|
@ -0,0 +1,15 @@
|
||||||
|
.emoji-value-list {
|
||||||
|
.value {
|
||||||
|
.shift-up-value-btn,
|
||||||
|
.shift-down-value-btn {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.shift-up-value-btn,
|
||||||
|
.shift-down-value-btn {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5010,6 +5010,10 @@ en:
|
||||||
reset: "reset"
|
reset: "reset"
|
||||||
none: "none"
|
none: "none"
|
||||||
site_settings:
|
site_settings:
|
||||||
|
emoji_list:
|
||||||
|
invalid_input: "Emoji list should only contain valid emoji names, eg: hugs"
|
||||||
|
add_emoji_button:
|
||||||
|
label: "Add Emoji"
|
||||||
title: "Settings"
|
title: "Settings"
|
||||||
no_results: "No results found."
|
no_results: "No results found."
|
||||||
more_than_30_results: "There are more than 30 results. Please refine your search or select a category."
|
more_than_30_results: "There are more than 30 results. Please refine your search or select a category."
|
||||||
|
|
|
@ -35,7 +35,8 @@ class SiteSettings::TypeSupervisor
|
||||||
group_list: 20,
|
group_list: 20,
|
||||||
tag_list: 21,
|
tag_list: 21,
|
||||||
color: 22,
|
color: 22,
|
||||||
simple_list: 23
|
simple_list: 23,
|
||||||
|
emoji_list: 24
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -66,6 +66,7 @@ module SvgSprite
|
||||||
"download",
|
"download",
|
||||||
"ellipsis-h",
|
"ellipsis-h",
|
||||||
"ellipsis-v",
|
"ellipsis-v",
|
||||||
|
"emoji-icon",
|
||||||
"envelope",
|
"envelope",
|
||||||
"envelope-square",
|
"envelope-square",
|
||||||
"exchange-alt",
|
"exchange-alt",
|
||||||
|
|
|
@ -91,6 +91,9 @@ describe SiteSettings::TypeSupervisor do
|
||||||
it "'simple_list' should be at the right position" do
|
it "'simple_list' should be at the right position" do
|
||||||
expect(SiteSettings::TypeSupervisor.types[:simple_list]).to eq(23)
|
expect(SiteSettings::TypeSupervisor.types[:simple_list]).to eq(23)
|
||||||
end
|
end
|
||||||
|
it "'emoji_list' should be at the right position" do
|
||||||
|
expect(SiteSettings::TypeSupervisor.types[:emoji_list]).to eq(24)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
||||||
|
<symbol overflow="visible" viewBox="0 0 216.3 152.3" id="emoji-icon">
|
||||||
|
<path d="M121.1 70.7c1.9 2 5.1 2.1 7.2.2l.2-.2 31.2-32.2c9.1-9.4 8.6-24.9-1.6-33.5-8.8-7.5-22-6.2-30.1 2.2l-3.2 3.3-3.2-3.3c-8.1-8.4-21.3-9.7-30.1-2.2-10.1 8.6-10.7 24.2-1.6 33.5l31.2 32.2zM53.5 45.3C23.9 45.3 0 69.2 0 98.8s23.9 53.5 53.5 53.5 53.5-24 53.5-53.5-23.9-53.5-53.5-53.5zm18.8 24.6c5.5 0 9.9 4.4 9.9 9.9s-4.4 9.9-9.9 9.9-9.9-4.4-9.9-9.9c-.1-5.5 4.4-9.9 9.9-9.9zm-38.2 0c5.5 0 9.9 4.4 9.9 9.9s-4.4 9.9-9.9 9.9-9.9-4.4-9.9-9.9 4.4-9.9 9.9-9.9zm52.4 45.4c-8.2 9.8-20.2 15.5-33.1 15.5s-24.9-5.6-33.1-15.5c-3.9-4.7 3.2-10.6 7.1-5.9 6.5 7.7 15.9 12.2 26 12.2s19.5-4.4 26-12.2c3.8-4.7 10.9 1.2 7.1 5.9zm129.8-22.5c0-6.4-6.3-9.5-13.3-9.5h-24c1.5-6 10.3-13.8 10.3-22.8 0-15.5-10.1-17.2-15.2-17.2-4.3 0-6.2 8.3-7.2 12.2-1.1 4.6-2.2 9.3-5.4 12.4-6.7 6.8-10.3 15.3-18.4 23.5-.7-1.8-2.6-3.1-4.7-3.1h-10.7c-2.8 0-5.1 2.2-5.1 4.9V142c0 2.7 2.3 4.9 5.1 4.9h10.7c2.8 0 5.1-2.2 5.1-4.9v-.9c.3.1.6.2.9.2 3.3.1 7.8 1.9 11.1 3.4 6.7 3 15.1 6.7 25.3 6.7h.6c9 0 19.7-.1 24-6.3 1.8-2.5 2.7-4.4.5-9.9 5-3 7-10 1-15 8-5 8-13 1-17 6.1-1.9 8.5-6.6 8.4-10.4z"></path>
|
||||||
|
</symbol>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
Loading…
Reference in New Issue