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:
Ahmed Gagan 2021-04-07 19:02:05 +05:30 committed by GitHub
parent 105634435f
commit 2308a58113
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 302 additions and 1 deletions

View File

@ -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("|"));
},
});

View File

@ -27,6 +27,7 @@ const CUSTOM_TYPES = [
"tag_list",
"color",
"simple_list",
"emoji_list",
];
const AUTO_REFRESH_ON_SAVE = ["logo", "logo_small", "large_icon"];

View File

@ -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}}

View File

@ -0,0 +1,3 @@
{{emoji-value-list setting=setting values=value}}
<div class="desc">{{html-safe setting.description}}</div>
{{setting-validation-message message=validationMessage}}

View File

@ -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;

View File

@ -4,3 +4,6 @@
// Import all component-specific files
@import "desktop/components/_index";
// Import all admin-specific files
@import "desktop/admin/_index";

View File

@ -0,0 +1 @@
@import "admin_base";

View File

@ -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;
}
}
}
}

View File

@ -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."

View File

@ -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

View File

@ -66,6 +66,7 @@ module SvgSprite
"download",
"ellipsis-h",
"ellipsis-v",
"emoji-icon",
"envelope",
"envelope-square",
"exchange-alt",

View File

@ -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

7
vendor/assets/svg-icons/emoji.svg vendored Normal file
View File

@ -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