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",
|
||||
"color",
|
||||
"simple_list",
|
||||
"emoji_list",
|
||||
];
|
||||
|
||||
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 {
|
||||
.value {
|
||||
flex-flow: row wrap;
|
||||
|
|
|
@ -4,3 +4,6 @@
|
|||
|
||||
// Import all component-specific files
|
||||
@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"
|
||||
none: "none"
|
||||
site_settings:
|
||||
emoji_list:
|
||||
invalid_input: "Emoji list should only contain valid emoji names, eg: hugs"
|
||||
add_emoji_button:
|
||||
label: "Add Emoji"
|
||||
title: "Settings"
|
||||
no_results: "No results found."
|
||||
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,
|
||||
tag_list: 21,
|
||||
color: 22,
|
||||
simple_list: 23
|
||||
simple_list: 23,
|
||||
emoji_list: 24
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -66,6 +66,7 @@ module SvgSprite
|
|||
"download",
|
||||
"ellipsis-h",
|
||||
"ellipsis-v",
|
||||
"emoji-icon",
|
||||
"envelope",
|
||||
"envelope-square",
|
||||
"exchange-alt",
|
||||
|
|
|
@ -91,6 +91,9 @@ describe SiteSettings::TypeSupervisor do
|
|||
it "'simple_list' should be at the right position" do
|
||||
expect(SiteSettings::TypeSupervisor.types[:simple_list]).to eq(23)
|
||||
end
|
||||
it "'emoji_list' should be at the right position" do
|
||||
expect(SiteSettings::TypeSupervisor.types[:emoji_list]).to eq(24)
|
||||
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