js: Convert static/js/composebox_typeahead.js to ES6 module.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg 2021-02-28 12:33:26 -08:00 committed by Tim Abbott
parent 5cc1f8d289
commit d8c793f791
10 changed files with 114 additions and 118 deletions

View File

@ -127,7 +127,6 @@
"globals": { "globals": {
"$": false, "$": false,
"blueslip": false, "blueslip": false,
"composebox_typeahead": false,
"csrf_token": false, "csrf_token": false,
"current_msg_list": true, "current_msg_list": true,
"dropdown_list_widget": false, "dropdown_list_widget": false,

View File

@ -70,7 +70,7 @@ const ct = composebox_typeahead;
// Use a slightly larger value than what's user-facing // Use a slightly larger value than what's user-facing
// to facilitate testing different combinations of // to facilitate testing different combinations of
// broadcast-mentions/persons/groups. // broadcast-mentions/persons/groups.
ct.max_num_items = 15; ct.__Rewire__("max_num_items", 15);
const mention_all = ct.broadcast_mentions()[0]; const mention_all = ct.broadcast_mentions()[0];
assert.equal(mention_all.email, "all"); assert.equal(mention_all.email, "all");
@ -1146,7 +1146,7 @@ run_test("begins_typeahead", () => {
function get_values(input, rest) { function get_values(input, rest) {
// Stub out split_at_cursor that uses $(':focus') // Stub out split_at_cursor that uses $(':focus')
ct.split_at_cursor = () => [input, rest]; ct.__Rewire__("split_at_cursor", () => [input, rest]);
const values = ct.get_candidates.call(begin_typehead_this, input); const values = ct.get_candidates.call(begin_typehead_this, input);
return values; return values;
} }
@ -1526,7 +1526,7 @@ run_test("message people", () => {
let results; let results;
compose_state.__Rewire__("stream_name", () => undefined); compose_state.__Rewire__("stream_name", () => undefined);
ct.max_num_items = 2; ct.__Rewire__("max_num_items", 2);
/* /*
We will simulate that we talk to Hal and Harry, We will simulate that we talk to Hal and Harry,

View File

@ -29,7 +29,8 @@ rewiremock("../../static/js/attachments_ui").with(attachments_ui);
const bot_data = {__esModule: true}; const bot_data = {__esModule: true};
rewiremock("../../static/js/bot_data").with(bot_data); rewiremock("../../static/js/bot_data").with(bot_data);
rewiremock("../../static/js/compose").with({}); rewiremock("../../static/js/compose").with({});
const composebox_typeahead = set_global("composebox_typeahead", {}); const composebox_typeahead = {__esModule: true};
rewiremock("../../static/js/composebox_typeahead").with(composebox_typeahead);
set_global("current_msg_list", {}); set_global("current_msg_list", {});
const emoji_picker = {__esModule: true}; const emoji_picker = {__esModule: true};
rewiremock("../../static/js/emoji_picker").with(emoji_picker); rewiremock("../../static/js/emoji_picker").with(emoji_picker);

View File

@ -117,7 +117,6 @@ const util = zrequire("util");
const upload = zrequire("upload"); const upload = zrequire("upload");
const compose = zrequire("compose"); const compose = zrequire("compose");
zrequire("composebox_typeahead");
run_test("initialize_everything", () => { run_test("initialize_everything", () => {
util.is_mobile = () => false; util.is_mobile = () => false;

View File

@ -18,7 +18,6 @@ import "../fold_dict";
import "../setup"; import "../setup";
import "../message_list"; import "../message_list";
import "../reload"; import "../reload";
import "../composebox_typeahead";
import "../hotkey"; import "../hotkey";
import "../notifications"; import "../notifications";
import "../message_events"; import "../message_events";

View File

@ -1,29 +1,27 @@
"use strict"; import autosize from "autosize";
import {formatISO} from "date-fns";
import ConfirmDatePlugin from "flatpickr/dist/plugins/confirmDate/confirmDate";
import _ from "lodash";
const autosize = require("autosize"); import pygments_data from "../generated/pygments_data.json";
const {formatISO} = require("date-fns"); import * as emoji from "../shared/js/emoji";
const ConfirmDatePlugin = require("flatpickr/dist/plugins/confirmDate/confirmDate"); import * as typeahead from "../shared/js/typeahead";
const _ = require("lodash");
const pygments_data = require("../generated/pygments_data.json"); import * as channel from "./channel";
const emoji = require("../shared/js/emoji"); import * as compose from "./compose";
const typeahead = require("../shared/js/typeahead"); import * as compose_pm_pill from "./compose_pm_pill";
import * as compose_state from "./compose_state";
const channel = require("./channel"); import * as compose_ui from "./compose_ui";
const compose = require("./compose"); import * as message_store from "./message_store";
const compose_pm_pill = require("./compose_pm_pill"); import * as people from "./people";
const compose_state = require("./compose_state"); import * as rows from "./rows";
const compose_ui = require("./compose_ui"); import * as settings_data from "./settings_data";
const message_store = require("./message_store"); import * as stream_data from "./stream_data";
const people = require("./people"); import * as stream_topic_history from "./stream_topic_history";
const rows = require("./rows"); import * as timerender from "./timerender";
const settings_data = require("./settings_data"); import * as typeahead_helper from "./typeahead_helper";
const stream_data = require("./stream_data"); import * as user_groups from "./user_groups";
const stream_topic_history = require("./stream_topic_history"); import * as user_pill from "./user_pill";
const timerender = require("./timerender");
const typeahead_helper = require("./typeahead_helper");
const user_groups = require("./user_groups");
const user_pill = require("./user_pill");
//************************************ //************************************
// AN IMPORTANT NOTE ABOUT TYPEAHEADS // AN IMPORTANT NOTE ABOUT TYPEAHEADS
@ -38,38 +36,38 @@ const user_pill = require("./user_pill");
// This is what we use for PM/compose typeaheads. // This is what we use for PM/compose typeaheads.
// We export it to allow tests to mock it. // We export it to allow tests to mock it.
exports.max_num_items = 8; export const max_num_items = 8;
exports.emoji_collection = []; export let emoji_collection = [];
exports.update_emoji_data = function () { export function update_emoji_data() {
exports.emoji_collection = []; emoji_collection = [];
for (const emoji_dict of emoji.emojis_by_name.values()) { for (const emoji_dict of emoji.emojis_by_name.values()) {
if (emoji_dict.is_realm_emoji === true) { if (emoji_dict.is_realm_emoji === true) {
exports.emoji_collection.push({ emoji_collection.push({
emoji_name: emoji_dict.name, emoji_name: emoji_dict.name,
emoji_url: emoji_dict.url, emoji_url: emoji_dict.url,
is_realm_emoji: true, is_realm_emoji: true,
}); });
} else { } else {
for (const alias of emoji_dict.aliases) { for (const alias of emoji_dict.aliases) {
exports.emoji_collection.push({ emoji_collection.push({
emoji_name: alias, emoji_name: alias,
emoji_code: emoji_dict.emoji_code, emoji_code: emoji_dict.emoji_code,
}); });
} }
} }
} }
}; }
exports.topics_seen_for = function (stream_name) { export function topics_seen_for(stream_name) {
const stream_id = stream_data.get_stream_id(stream_name); const stream_id = stream_data.get_stream_id(stream_name);
if (!stream_id) { if (!stream_id) {
return []; return [];
} }
const topic_names = stream_topic_history.get_recent_topic_names(stream_id); const topic_names = stream_topic_history.get_recent_topic_names(stream_id);
return topic_names; return topic_names;
}; }
function get_language_matcher(query) { function get_language_matcher(query) {
query = query.toLowerCase(); query = query.toLowerCase();
@ -78,7 +76,7 @@ function get_language_matcher(query) {
}; };
} }
exports.query_matches_person = function (query, person) { export function query_matches_person(query, person) {
if (!settings_data.show_email()) { if (!settings_data.show_email()) {
return typeahead.query_matches_source_attrs(query, person, ["full_name"], " "); return typeahead.query_matches_source_attrs(query, person, ["full_name"], " ");
} }
@ -87,7 +85,7 @@ exports.query_matches_person = function (query, person) {
email_attr = "delivery_email"; email_attr = "delivery_email";
} }
return typeahead.query_matches_source_attrs(query, person, ["full_name", email_attr], " "); return typeahead.query_matches_source_attrs(query, person, ["full_name", email_attr], " ");
}; }
function query_matches_name_description(query, user_group_or_stream) { function query_matches_name_description(query, user_group_or_stream) {
return typeahead.query_matches_source_attrs( return typeahead.query_matches_source_attrs(
@ -127,7 +125,7 @@ function get_topic_matcher(query) {
}; };
} }
exports.should_enter_send = function (e) { export function should_enter_send(e) {
const has_non_shift_modifier_key = e.ctrlKey || e.metaKey || e.altKey; const has_non_shift_modifier_key = e.ctrlKey || e.metaKey || e.altKey;
const has_modifier_key = e.shiftKey || has_non_shift_modifier_key; const has_modifier_key = e.shiftKey || has_non_shift_modifier_key;
let this_enter_sends; let this_enter_sends;
@ -147,9 +145,9 @@ exports.should_enter_send = function (e) {
this_enter_sends = has_non_shift_modifier_key; this_enter_sends = has_non_shift_modifier_key;
} }
return this_enter_sends; return this_enter_sends;
}; }
exports.handle_enter = function (textarea, e) { export function handle_enter(textarea, e) {
// Used only if Enter doesn't send. // Used only if Enter doesn't send.
// Since this Enter doesn't send, we just want to do // Since this Enter doesn't send, we just want to do
@ -180,7 +178,7 @@ exports.handle_enter = function (textarea, e) {
return; return;
} }
// Fall through to native browser behavior, otherwise. // Fall through to native browser behavior, otherwise.
}; }
let nextFocus = false; let nextFocus = false;
@ -205,13 +203,13 @@ function handle_keydown(e) {
// This if branch is only here to make Tab+Enter work on Safari, // This if branch is only here to make Tab+Enter work on Safari,
// which does not make <button>s tab-accessible by default // which does not make <button>s tab-accessible by default
// (even if we were to set tabindex=0). // (even if we were to set tabindex=0).
if (!exports.should_enter_send(e)) { if (!should_enter_send(e)) {
$("#compose-send-button").trigger("focus"); $("#compose-send-button").trigger("focus");
e.preventDefault(); e.preventDefault();
} }
} else { } else {
// Enter // Enter
if (exports.should_enter_send(e)) { if (should_enter_send(e)) {
e.preventDefault(); e.preventDefault();
if (!$("#compose-send-button").prop("disabled")) { if (!$("#compose-send-button").prop("disabled")) {
$("#compose-send-button").prop("disabled", true); $("#compose-send-button").prop("disabled", true);
@ -220,7 +218,7 @@ function handle_keydown(e) {
return; return;
} }
exports.handle_enter($("#compose-textarea"), e); handle_enter($("#compose-textarea"), e);
} }
} else if (on_stream || on_topic || on_pm) { } else if (on_stream || on_topic || on_pm) {
// Prevent the form from submitting // Prevent the form from submitting
@ -251,12 +249,12 @@ function handle_keyup(e) {
} }
} }
exports.split_at_cursor = function (query, input) { export function split_at_cursor(query, input) {
const cursor = input.caret(); const cursor = input.caret();
return [query.slice(0, cursor), query.slice(cursor)]; return [query.slice(0, cursor), query.slice(cursor)];
}; }
exports.tokenize_compose_str = function (s) { export function tokenize_compose_str(s) {
// This basically finds a token like "@alic" or // This basically finds a token like "@alic" or
// "#Veron" as close to the end of the string as it // "#Veron" as close to the end of the string as it
// can find it. It wants to find white space or // can find it. It wants to find white space or
@ -322,9 +320,9 @@ exports.tokenize_compose_str = function (s) {
} }
return ""; return "";
}; }
exports.broadcast_mentions = function () { export function broadcast_mentions() {
return ["all", "everyone", "stream"].map((mention, idx) => ({ return ["all", "everyone", "stream"].map((mention, idx) => ({
special_item_text: i18n.t("__wildcard_mention_token__ (Notify stream)", { special_item_text: i18n.t("__wildcard_mention_token__ (Notify stream)", {
wildcard_mention_token: mention, wildcard_mention_token: mention,
@ -342,7 +340,7 @@ exports.broadcast_mentions = function () {
// used for sorting // used for sorting
idx, idx,
})); }));
}; }
function filter_mention_name(current_token) { function filter_mention_name(current_token) {
if (current_token.startsWith("**")) { if (current_token.startsWith("**")) {
@ -371,7 +369,7 @@ function should_show_custom_query(query, items) {
return !matched; return !matched;
} }
exports.slash_commands = [ export const slash_commands = [
{ {
text: i18n.t("/dark (Toggle night mode)"), text: i18n.t("/dark (Toggle night mode)"),
name: "dark", name: "dark",
@ -414,26 +412,26 @@ exports.slash_commands = [
}, },
]; ];
exports.filter_and_sort_mentions = function (is_silent, query, opts) { export function filter_and_sort_mentions(is_silent, query, opts) {
opts = { opts = {
want_broadcast: !is_silent, want_broadcast: !is_silent,
want_groups: !is_silent, want_groups: !is_silent,
filter_pills: false, filter_pills: false,
...opts, ...opts,
}; };
return exports.get_person_suggestions(query, opts); return get_person_suggestions(query, opts);
}; }
exports.get_pm_people = function (query) { export function get_pm_people(query) {
const opts = { const opts = {
want_broadcast: false, want_broadcast: false,
want_groups: true, want_groups: true,
filter_pills: true, filter_pills: true,
}; };
return exports.get_person_suggestions(query, opts); return get_person_suggestions(query, opts);
}; }
exports.get_person_suggestions = function (query, opts) { export function get_person_suggestions(query, opts) {
query = typeahead.clean_query_lowercase(query); query = typeahead.clean_query_lowercase(query);
function filter_persons(all_persons) { function filter_persons(all_persons) {
@ -446,9 +444,9 @@ exports.get_person_suggestions = function (query, opts) {
} }
if (opts.want_broadcast) { if (opts.want_broadcast) {
persons = persons.concat(exports.broadcast_mentions()); persons = persons.concat(broadcast_mentions());
} }
return persons.filter((item) => exports.query_matches_person(query, item)); return persons.filter((item) => query_matches_person(query, item));
} }
let groups; let groups;
@ -483,7 +481,7 @@ exports.get_person_suggestions = function (query, opts) {
persons who match on prefix to groups who persons who match on prefix to groups who
match on prefix.) match on prefix.)
*/ */
const cutoff_length = exports.max_num_items; const cutoff_length = max_num_items;
const filtered_message_persons = filter_persons(people.get_active_message_people()); const filtered_message_persons = filter_persons(people.get_active_message_people());
@ -501,11 +499,11 @@ exports.get_person_suggestions = function (query, opts) {
opts.stream, opts.stream,
opts.topic, opts.topic,
filtered_groups, filtered_groups,
exports.max_num_items, max_num_items,
); );
}; }
exports.get_stream_topic_data = (hacky_this) => { export function get_stream_topic_data(hacky_this) {
const opts = {}; const opts = {};
const message_row = hacky_this.$element.closest(".message_row"); const message_row = hacky_this.$element.closest(".message_row");
if (message_row.length === 1) { if (message_row.length === 1) {
@ -520,9 +518,9 @@ exports.get_stream_topic_data = (hacky_this) => {
opts.topic = compose_state.topic(); opts.topic = compose_state.topic();
} }
return opts; return opts;
}; }
exports.get_sorted_filtered_items = function (query) { export function get_sorted_filtered_items(query) {
/* /*
This is just a "glue" function to work This is just a "glue" function to work
around bootstrap. We want to control these around bootstrap. We want to control these
@ -546,7 +544,7 @@ exports.get_sorted_filtered_items = function (query) {
several years ago.) several years ago.)
*/ */
const fetcher = exports.get_candidates.bind(this); const fetcher = get_candidates.bind(this);
const big_results = fetcher(query); const big_results = fetcher(query);
if (!big_results) { if (!big_results) {
@ -558,28 +556,28 @@ exports.get_sorted_filtered_items = function (query) {
const completing = this.completing; const completing = this.completing;
const token = this.token; const token = this.token;
const opts = exports.get_stream_topic_data(this); const opts = get_stream_topic_data(this);
if (completing === "mention" || completing === "silent_mention") { if (completing === "mention" || completing === "silent_mention") {
return exports.filter_and_sort_mentions(big_results.is_silent, token, opts); return filter_and_sort_mentions(big_results.is_silent, token, opts);
} }
return exports.filter_and_sort_candidates(completing, big_results, token); return filter_and_sort_candidates(completing, big_results, token);
}; }
exports.filter_and_sort_candidates = function (completing, candidates, token) { export function filter_and_sort_candidates(completing, candidates, token) {
const matcher = exports.compose_content_matcher(completing, token); const matcher = compose_content_matcher(completing, token);
const small_results = candidates.filter((item) => matcher(item)); const small_results = candidates.filter((item) => matcher(item));
const sorted_results = exports.sort_results(completing, small_results, token); const sorted_results = sort_results(completing, small_results, token);
return sorted_results; return sorted_results;
}; }
exports.get_candidates = function (query) { export function get_candidates(query) {
const split = exports.split_at_cursor(query, this.$element); const split = split_at_cursor(query, this.$element);
let current_token = exports.tokenize_compose_str(split[0]); let current_token = tokenize_compose_str(split[0]);
if (current_token === "") { if (current_token === "") {
return false; return false;
} }
@ -635,7 +633,7 @@ exports.get_candidates = function (query) {
} }
this.completing = "emoji"; this.completing = "emoji";
this.token = current_token.slice(1); this.token = current_token.slice(1);
return exports.emoji_collection; return emoji_collection;
} }
if (this.options.completions.mention && current_token[0] === "@") { if (this.options.completions.mention && current_token[0] === "@") {
@ -658,7 +656,7 @@ exports.get_candidates = function (query) {
} }
function get_slash_commands_data() { function get_slash_commands_data() {
const commands = exports.slash_commands; const commands = slash_commands;
return commands; return commands;
} }
@ -711,7 +709,7 @@ exports.get_candidates = function (query) {
if (tokens[1]) { if (tokens[1]) {
const stream_name = tokens[1]; const stream_name = tokens[1];
this.token = tokens[2] || ""; this.token = tokens[2] || "";
const topic_list = exports.topics_seen_for(stream_name); const topic_list = topics_seen_for(stream_name);
if (should_show_custom_query(this.token, topic_list)) { if (should_show_custom_query(this.token, topic_list)) {
topic_list.push(this.token); topic_list.push(this.token);
} }
@ -727,9 +725,9 @@ exports.get_candidates = function (query) {
} }
} }
return false; return false;
}; }
exports.content_highlighter = function (item) { export function content_highlighter(item) {
switch (this.completing) { switch (this.completing) {
case "emoji": case "emoji":
return typeahead_helper.render_emoji(item); return typeahead_helper.render_emoji(item);
@ -754,7 +752,7 @@ exports.content_highlighter = function (item) {
default: default:
return undefined; return undefined;
} }
}; }
const show_flatpickr = (element, callback, default_timestamp) => { const show_flatpickr = (element, callback, default_timestamp) => {
const flatpickr_input = $("<input id='#timestamp_flatpickr'>"); const flatpickr_input = $("<input id='#timestamp_flatpickr'>");
@ -784,8 +782,8 @@ const show_flatpickr = (element, callback, default_timestamp) => {
container.find(".flatpickr-monthDropdown-months").trigger("focus"); container.find(".flatpickr-monthDropdown-months").trigger("focus");
}; };
exports.content_typeahead_selected = function (item, event) { export function content_typeahead_selected(item, event) {
const pieces = exports.split_at_cursor(this.query, this.$element); const pieces = split_at_cursor(this.query, this.$element);
let beginning = pieces[0]; let beginning = pieces[0];
let rest = pieces[1]; let rest = pieces[1];
const textbox = this.$element; const textbox = this.$element;
@ -923,9 +921,9 @@ exports.content_typeahead_selected = function (item, event) {
compose_ui.autosize_textarea(textbox); compose_ui.autosize_textarea(textbox);
}, 0); }, 0);
return beginning + rest; return beginning + rest;
}; }
exports.compose_content_matcher = function (completing, token) { export function compose_content_matcher(completing, token) {
switch (completing) { switch (completing) {
case "emoji": case "emoji":
return typeahead.get_emoji_matcher(token); return typeahead.get_emoji_matcher(token);
@ -949,9 +947,9 @@ exports.compose_content_matcher = function (completing, token) {
return undefined; return undefined;
} }
}; };
}; }
exports.sort_results = function (completing, matches, token) { export function sort_results(completing, matches, token) {
switch (completing) { switch (completing) {
case "emoji": case "emoji":
return typeahead.sort_emojis(matches, token); return typeahead.sort_emojis(matches, token);
@ -970,24 +968,24 @@ exports.sort_results = function (completing, matches, token) {
default: default:
return undefined; return undefined;
} }
}; }
exports.compose_automated_selection = function () { export function compose_automated_selection() {
if (this.completing === "topic_jump") { if (this.completing === "topic_jump") {
// automatically jump inside stream mention on typing > just after // automatically jump inside stream mention on typing > just after
// a stream mention, to begin stream+topic mention typeahead (topic_list). // a stream mention, to begin stream+topic mention typeahead (topic_list).
return true; return true;
} }
return false; return false;
}; }
exports.compose_trigger_selection = function (event) { export function compose_trigger_selection(event) {
if (this.completing === "stream" && event.key === ">") { if (this.completing === "stream" && event.key === ">") {
// complete stream typeahead partially to immediately start the topic_list typeahead. // complete stream typeahead partially to immediately start the topic_list typeahead.
return true; return true;
} }
return false; return false;
}; }
function get_header_html() { function get_header_html() {
let tip_text = ""; let tip_text = "";
@ -1012,7 +1010,7 @@ function get_header_html() {
return `<em>${_.escape(tip_text)}</em>`; return `<em>${_.escape(tip_text)}</em>`;
} }
exports.initialize_compose_typeahead = function (selector) { export function initialize_compose_typeahead(selector) {
const completions = { const completions = {
mention: true, mention: true,
emoji: true, emoji: true,
@ -1025,32 +1023,32 @@ exports.initialize_compose_typeahead = function (selector) {
}; };
$(selector).typeahead({ $(selector).typeahead({
items: exports.max_num_items, items: max_num_items,
dropup: true, dropup: true,
fixed: true, fixed: true,
// Performance note: We have trivial matcher/sorters to do // Performance note: We have trivial matcher/sorters to do
// matching and sorting inside the `source` field to avoid // matching and sorting inside the `source` field to avoid
// O(n) behavior in the number of users in the organization // O(n) behavior in the number of users in the organization
// inside the typeahead library. // inside the typeahead library.
source: exports.get_sorted_filtered_items, source: get_sorted_filtered_items,
highlighter: exports.content_highlighter, highlighter: content_highlighter,
matcher() { matcher() {
return true; return true;
}, },
sorter(items) { sorter(items) {
return items; return items;
}, },
updater: exports.content_typeahead_selected, updater: content_typeahead_selected,
stopAdvance: true, // Do not advance to the next field on a Tab or Enter stopAdvance: true, // Do not advance to the next field on a Tab or Enter
completions, completions,
automated: exports.compose_automated_selection, automated: compose_automated_selection,
trigger_selection: exports.compose_trigger_selection, trigger_selection: compose_trigger_selection,
header: get_header_html, header: get_header_html,
}); });
}; }
exports.initialize = function () { export function initialize() {
exports.update_emoji_data(); update_emoji_data();
// These handlers are at the "form" level so that they are called after typeahead // These handlers are at the "form" level so that they are called after typeahead
$("form#send_message_form").on("keydown", handle_keydown); $("form#send_message_form").on("keydown", handle_keydown);
@ -1101,7 +1099,7 @@ exports.initialize = function () {
$("#stream_message_recipient_topic").typeahead({ $("#stream_message_recipient_topic").typeahead({
source() { source() {
const stream_name = compose_state.stream_name(); const stream_name = compose_state.stream_name();
return exports.topics_seen_for(stream_name); return topics_seen_for(stream_name);
}, },
items: 3, items: 3,
fixed: true, fixed: true,
@ -1118,8 +1116,8 @@ exports.initialize = function () {
}); });
$("#private_message_recipient").typeahead({ $("#private_message_recipient").typeahead({
source: exports.get_pm_people, source: get_pm_people,
items: exports.max_num_items, items: max_num_items,
dropup: true, dropup: true,
fixed: true, fixed: true,
highlighter(item) { highlighter(item) {
@ -1154,13 +1152,11 @@ exports.initialize = function () {
stopAdvance: true, // Do not advance to the next field on a Tab or Enter stopAdvance: true, // Do not advance to the next field on a Tab or Enter
}); });
exports.initialize_compose_typeahead("#compose-textarea"); initialize_compose_typeahead("#compose-textarea");
$("#private_message_recipient").on("blur", function () { $("#private_message_recipient").on("blur", function () {
const val = $(this).val(); const val = $(this).val();
const recipients = typeahead_helper.get_cleaned_pm_recipients(val); const recipients = typeahead_helper.get_cleaned_pm_recipients(val);
$(this).val(recipients.join(", ")); $(this).val(recipients.join(", "));
}); });
}; }
window.composebox_typeahead = exports;

View File

@ -4,7 +4,6 @@
// to TS. // to TS.
declare let blueslip: any; declare let blueslip: any;
declare let composebox_typeahead: any;
declare let csrf_token: any; declare let csrf_token: any;
declare let current_msg_list: any; declare let current_msg_list: any;
declare let emoji: any; declare let emoji: any;

View File

@ -6,6 +6,7 @@ import render_topic_edit_form from "../templates/topic_edit_form.hbs";
import * as channel from "./channel"; import * as channel from "./channel";
import * as compose from "./compose"; import * as compose from "./compose";
import * as compose_actions from "./compose_actions"; import * as compose_actions from "./compose_actions";
import * as composebox_typeahead from "./composebox_typeahead";
import * as condense from "./condense"; import * as condense from "./condense";
import * as echo from "./echo"; import * as echo from "./echo";
import * as loading from "./loading"; import * as loading from "./loading";

View File

@ -7,6 +7,7 @@ import * as attachments_ui from "./attachments_ui";
import * as bot_data from "./bot_data"; import * as bot_data from "./bot_data";
import * as compose from "./compose"; import * as compose from "./compose";
import * as compose_fade from "./compose_fade"; import * as compose_fade from "./compose_fade";
import * as composebox_typeahead from "./composebox_typeahead";
import * as emoji_picker from "./emoji_picker"; import * as emoji_picker from "./emoji_picker";
import * as hotspots from "./hotspots"; import * as hotspots from "./hotspots";
import * as markdown from "./markdown"; import * as markdown from "./markdown";

View File

@ -12,6 +12,7 @@ import * as bot_data from "./bot_data";
import * as click_handlers from "./click_handlers"; import * as click_handlers from "./click_handlers";
import * as compose from "./compose"; import * as compose from "./compose";
import * as compose_pm_pill from "./compose_pm_pill"; import * as compose_pm_pill from "./compose_pm_pill";
import * as composebox_typeahead from "./composebox_typeahead";
import * as condense from "./condense"; import * as condense from "./condense";
import * as copy_and_paste from "./copy_and_paste"; import * as copy_and_paste from "./copy_and_paste";
import * as drafts from "./drafts"; import * as drafts from "./drafts";