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
diff --git a/app/assets/javascripts/admin/addon/components/emoji-value-list.js b/app/assets/javascripts/admin/addon/components/emoji-value-list.js
new file mode 100644
index 0000000..e7096a7
--- /dev/null
+++ b/app/assets/javascripts/admin/addon/components/emoji-value-list.js
@@ -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("|"));
+ },
+});
diff --git a/app/assets/javascripts/admin/addon/mixins/setting-component.js b/app/assets/javascripts/admin/addon/mixins/setting-component.js
index d96bd40..fc6c6c2 100644
--- a/app/assets/javascripts/admin/addon/mixins/setting-component.js
+++ b/app/assets/javascripts/admin/addon/mixins/setting-component.js
@@ -27,6 +27,7 @@ const CUSTOM_TYPES = [
"tag_list",
"color",
"simple_list",
+ "emoji_list",
];
const AUTO_REFRESH_ON_SAVE = ["logo", "logo_small", "large_icon"];
diff --git a/app/assets/javascripts/admin/addon/templates/components/emoji-value-list.hbs b/app/assets/javascripts/admin/addon/templates/components/emoji-value-list.hbs
new file mode 100644
index 0000000..aeed56f
--- /dev/null
+++ b/app/assets/javascripts/admin/addon/templates/components/emoji-value-list.hbs
@@ -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}}
diff --git a/app/assets/javascripts/admin/addon/templates/components/site-settings/emoji-list.hbs b/app/assets/javascripts/admin/addon/templates/components/site-settings/emoji-list.hbs
new file mode 100644
index 0000000..50d1d18
--- /dev/null
+++ b/app/assets/javascripts/admin/addon/templates/components/site-settings/emoji-list.hbs
@@ -0,0 +1,3 @@
+{{emoji-value-list setting=setting values=value}}
+<div class="desc">{{html-safe setting.description}}</div>
+{{setting-validation-message message=validationMessage}}
diff --git a/app/assets/stylesheets/common/admin/admin_base.scss b/app/assets/stylesheets/common/admin/admin_base.scss
index a65d7ff..e920bb7 100644
--- a/app/assets/stylesheets/common/admin/admin_base.scss
+++ b/app/assets/stylesheets/common/admin/admin_base.scss
@@ -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;
[... diff too long, it was truncated ...]
GitHub sha: 2308a581