along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
- @template core_message/emoji/picker
+ @template core/emoji/picker
This template will render the emoji picker.
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
- @template core_message/emoji/picker
+ @template core/emoji/picker
This template will render the emoji picker.
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
- @template core_message/emoji/picker
+ @template core/emoji/picker
This template will render the emoji picker.
'core_message/message_drawer_view_conversation_state_manager',
'core_message/message_drawer_router',
'core_message/message_drawer_routes',
+ 'core/emoji/picker'
],
function(
$,
Renderer,
StateManager,
MessageDrawerRouter,
- MessageDrawerRoutes
+ MessageDrawerRoutes,
+ initialiseEmojiPicker
) {
// Contains a cache of all view states that have been loaded so far
};
};
+ /**
+ * Handle clicking on the emoji toggle button.
+ *
+ * @param {Object} e The event
+ * @param {Object} data The custom interaction event data
+ */
+ var handleToggleEmojiPicker = function(e, data) {
+ var newState = StateManager.setShowEmojiPicker(viewState, !viewState.showEmojiPicker);
+ render(newState);
+ data.originalEvent.preventDefault();
+ };
+
+ /**
+ * Handle clicking outside the emoji picker to close it.
+ *
+ * @param {Object} e The event
+ */
+ var handleCloseEmojiPicker = function(e) {
+ var target = $(e.target);
+
+ if (
+ viewState.showEmojiPicker &&
+ !target.closest(SELECTORS.EMOJI_PICKER_CONTAINER).length &&
+ !target.closest(SELECTORS.TOGGLE_EMOJI_PICKER_BUTTON).length
+ ) {
+ var newState = StateManager.setShowEmojiPicker(viewState, false);
+ render(newState);
+ }
+ };
+
/**
* Listen to, and handle events for conversations.
*
var registerEventListeners = function(namespace, header, body, footer) {
var isLoadingMoreMessages = false;
var messagesContainer = getMessagesContainer(body);
+ var emojiPickerElement = footer.find(SELECTORS.EMOJI_PICKER);
+ var messageTextArea = footer.find(SELECTORS.MESSAGE_TEXT_AREA);
var headerActivateHandlers = [
[SELECTORS.ACTION_REQUEST_BLOCK, generateConfirmActionHandler(requestBlockUser)],
[SELECTORS.ACTION_REQUEST_UNBLOCK, generateConfirmActionHandler(requestUnblockUser)],
];
var footerActivateHandlers = [
[SELECTORS.SEND_MESSAGE_BUTTON, handleSendMessage],
+ [SELECTORS.TOGGLE_EMOJI_PICKER_BUTTON, handleToggleEmojiPicker],
[SELECTORS.ACTION_REQUEST_DELETE_SELECTED_MESSAGES, generateConfirmActionHandler(requestDeleteSelectedMessages)],
[SELECTORS.ACTION_REQUEST_ADD_CONTACT, generateConfirmActionHandler(requestAddContact)],
[SELECTORS.ACTION_REQUEST_UNBLOCK, generateConfirmActionHandler(requestUnblockUser)],
AutoRows.init(footer);
+ initialiseEmojiPicker(emojiPickerElement[0], function(emoji) {
+ var newState = StateManager.setShowEmojiPicker(viewState, !viewState.showEmojiPicker);
+ render(newState);
+
+ messageTextArea.focus();
+ var cursorPos = messageTextArea.prop('selectionStart');
+ var currentText = messageTextArea.val();
+ var textBefore = currentText.substring(0, cursorPos);
+ var textAfter = currentText.substring(cursorPos, currentText.length);
+
+ messageTextArea.val(textBefore + emoji + textAfter);
+ // Set the cursor position to after the inserted emoji.
+ messageTextArea.prop('selectionStart', cursorPos + emoji.length);
+ messageTextArea.prop('selectionEnd', cursorPos + emoji.length);
+ });
+
CustomEvents.define(header, [
CustomEvents.events.activate
]);
]);
CustomEvents.define(footer, [
CustomEvents.events.activate,
- CustomEvents.events.enter
+ CustomEvents.events.enter,
+ CustomEvents.events.escape
]);
CustomEvents.define(messagesContainer, [
CustomEvents.events.scrollTop,
}
});
+ footer.on(CustomEvents.events.escape, SELECTORS.EMOJI_PICKER_CONTAINER, handleToggleEmojiPicker);
+ $(document.body).on('click', handleCloseEmojiPicker);
+
PubSub.subscribe(MessageDrawerEvents.ROUTE_CHANGED, function(newRouteData) {
if (newMessagesPollTimer) {
if (newRouteData.route != MessageDrawerRoutes.VIEW_CONVERSATION) {
DAY_MESSAGES_CONTAINER: '[data-region="day-messages-container"]',
DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE: '[data-region="delete-messages-for-all-users-toggle"]',
DELETE_MESSAGES_FOR_ALL_USERS_TOGGLE_CONTAINER: '[data-region="delete-messages-for-all-users-toggle-container"]',
+ EMOJI_PICKER_CONTAINER: '[data-region="emoji-picker-container"]',
+ EMOJI_PICKER: '[data-region="emoji-picker"]',
+ EMOJI_PICKER_SEARCH_INPUT: '[data-region="search-input"]',
ERROR_MESSAGE_CONTAINER: '[data-region="error-message-container"]',
ERROR_MESSAGE: '[data-region="error-message"]',
FAVOURITE_ICON_CONTAINER: '[data-region="favourite-icon-container"]',
TEXT: '[data-region="text"]',
TEXT_CONTAINER: '[data-region="text-container"]',
TIME_CREATED: '[data-region="time-created"]',
- TITLE: '[data-region="title"]'
+ TITLE: '[data-region="title"]',
+ TOGGLE_EMOJI_PICKER_BUTTON: '[data-action="toggle-emoji-picker"]'
};
var TEMPLATES = {
}
};
+ /**
+ * Determine if we should show the emoji picker.
+ *
+ * @param {Object} state The current state.
+ * @param {Object} newState The new state.
+ * @return {Bool|Null}
+ */
+ var buildShowEmojiPicker = function(state, newState) {
+ if (!state.showEmojiPicker && newState.showEmojiPicker) {
+ return true;
+ } else if (state.showEmojiPicker && !newState.showEmojiPicker) {
+ return false;
+ } else {
+ return null;
+ }
+ };
+
/**
* Get the user Object of user to be blocked if pending.
*
inEditMode: buildInEditMode,
selectedMessages: buildSelectedMessages,
isFavourite: buildIsFavourite,
- isMuted: buildIsMuted
+ isMuted: buildIsMuted,
+ showEmojiPicker: buildShowEmojiPicker
}
};
// These build functions are only applicable to private conversations.
getHeaderPlaceholderContainer(header).addClass('hidden');
};
+ /**
+ * Get the emoji picker container element.
+ *
+ * @param {Object} footer Conversation footer container element.
+ * @return {Object} The emoji picker container element.
+ */
+ var getEmojiPickerContainer = function(footer) {
+ return footer.find(SELECTORS.EMOJI_PICKER_CONTAINER);
+ };
+
/**
* Get a message element.
*
}
};
+ /**
+ * Hide or show the emoji picker.
+ *
+ * @param {Object} header The header container element.
+ * @param {Object} body The body container element.
+ * @param {Object} footer The footer container element.
+ * @param {Bool} show Should the emoji picker be visible.
+ */
+ var renderShowEmojiPicker = function(header, body, footer, show) {
+ var container = getEmojiPickerContainer(footer);
+
+ if (show) {
+ container.removeClass('hidden');
+ container.attr('aria-hidden', false);
+ container.find(SELECTORS.EMOJI_PICKER_SEARCH_INPUT).focus();
+ } else {
+ container.addClass('hidden');
+ container.attr('aria-hidden', true);
+ }
+ };
+
/**
* Show a confirmation dialogue
*
isFavourite: renderIsFavourite,
isMuted: renderIsMuted,
loadingConfirmAction: renderLoadingConfirmAction,
- inEditMode: renderInEditMode
+ inEditMode: renderInEditMode,
+ showEmojiPicker: renderShowEmojiPicker
},
{
// Scrolling should be last to make sure everything
pendingDeleteMessageIds: [],
pendingSendMessageIds: [],
pendingDeleteConversation: false,
- selectedMessageIds: []
+ selectedMessageIds: [],
+ showEmojiPicker: false
};
};
return newState;
};
+ /**
+ * Set the visibility of the emoji picker.
+ *
+ * @param {Object} state Current state.
+ * @param {Bool} show Should the emoji picker be shown.
+ * @return {Object} New state with array of pending delete message ids.
+ */
+ var setShowEmojiPicker = function(state, show) {
+ var newState = cloneState(state);
+ newState.showEmojiPicker = show;
+ return newState;
+ };
+
/**
* Set the state pending block userids.
*
setMessagesSendPendingById: setMessagesSendPendingById,
setMessagesSendSuccessById: setMessagesSendSuccessById,
setMessagesSendFailById: setMessagesSendFailById,
+ setShowEmojiPicker: setShowEmojiPicker,
addPendingBlockUsersById: addPendingBlockUsersById,
addPendingRemoveContactsById: addPendingRemoveContactsById,
addPendingUnblockUsersById: addPendingUnblockUsersById,
placeholder="{{#str}} writeamessage, core_message {{/str}}"
style="resize: none"
></textarea>
- <button
- class="btn btn-link btn-icon icon-size-3 ml-1 mt-auto"
- aria-label="{{#str}} sendmessage, core_message {{/str}}"
- data-action="send-message"
- >
- <span data-region="send-icon-container">{{#pix}} i/sendmessage, core {{/pix}}</span>
- <span class="hidden" data-region="loading-icon-container">{{> core/loading }}</span>
- </button>
+
+ <div class="position-relative d-flex flex-column">
+ <div
+ data-region="emoji-picker-container"
+ class="emoji-picker-container hidden"
+ aria-hidden="true"
+ >
+ {{> core/emoji/picker }}
+ </div>
+ <button
+ class="btn btn-link btn-icon icon-size-3 ml-1"
+ data-action="toggle-emoji-picker"
+ >
+ {{#pix}} e/emoticons, core{{/pix}}
+ </button>
+ <button
+ class="btn btn-link btn-icon icon-size-3 ml-1 mt-auto"
+ aria-label="{{#str}} sendmessage, core_message {{/str}}"
+ data-action="send-message"
+ >
+ <span data-region="send-icon-container">{{#pix}} i/sendmessage, core {{/pix}}</span>
+ <span class="hidden" data-region="loading-icon-container">{{> core/loading }}</span>
+ </button>
+ </div>
</div>
.footer-container {
flex-shrink: 0;
- overflow-x: hidden;
textarea {
direction: ltr;
box-shadow: 2px 2px 4px rgba(0, 0, 0, .08);
}
}
+
+.message-app {
+ .emoji-picker-container {
+ position: absolute;
+ top: -5px;
+ right: 5px;
+ transform: translateY(-100%);
+
+ .emoji-picker {
+ .picker-row {
+ // To override the button styling for the message app.
+ .emoji-button {
+ height: $picker-emoji-button-size;
+ width: $picker-emoji-button-size;
+ }
+ }
+ }
+
+ @include media-breakpoint-down(xs) {
+ right: -1 * map-get($spacers, 2);
+ }
+ }
+}
top: 0;
bottom: 0; }
.message-app .footer-container {
- flex-shrink: 0;
- overflow-x: hidden; }
+ flex-shrink: 0; }
.message-app .footer-container textarea {
direction: ltr; }
.message-app .matchtext {
.dir-rtl .message-drawer {
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.08); }
+.message-app .emoji-picker-container {
+ position: absolute;
+ top: -5px;
+ right: 5px;
+ transform: translateY(-100%); }
+ .message-app .emoji-picker-container .emoji-picker .picker-row .emoji-button {
+ height: 40px;
+ width: 40px; }
+ @media (max-width: 575.98px) {
+ .message-app .emoji-picker-container {
+ right: -0.5rem; } }
+
/* Question */
.questionbank h2 {
margin-top: 0; }
top: 0;
bottom: 0; }
.message-app .footer-container {
- flex-shrink: 0;
- overflow-x: hidden; }
+ flex-shrink: 0; }
.message-app .footer-container textarea {
direction: ltr; }
.message-app .matchtext {
.dir-rtl .message-drawer {
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.08); }
+.message-app .emoji-picker-container {
+ position: absolute;
+ top: -5px;
+ right: 5px;
+ transform: translateY(-100%); }
+ .message-app .emoji-picker-container .emoji-picker .picker-row .emoji-button {
+ height: 40px;
+ width: 40px; }
+ @media (max-width: 575.98px) {
+ .message-app .emoji-picker-container {
+ right: -0.5rem; } }
+
/* Question */
.questionbank h2 {
margin-top: 0; }