From f3fc26c6fff24f471f363cd9ddeda0a866ae0ec3 Mon Sep 17 00:00:00 2001 From: Sayam Samal Date: Thu, 10 Jul 2025 03:34:55 +0530 Subject: [PATCH] left_sidebar: Standardize topic list filter input. This follow-up commit replaces the current left sidebar topic list filter input implementation with the redesigned input_wrapper component. This commit also serves as the base for supporting inputs using the search_pill_widget, and thus adjusts the previously defined logic at certain places to ensure that the input pills are handled and displayed accurately. Fixes part of #34476. --- web/src/click_handlers.ts | 2 +- web/src/inputs.ts | 23 +++++++++++++++++- web/src/topic_list.ts | 31 +++++++++--------------- web/styles/inputs.css | 42 ++++++++++++++++++++++++++++++--- web/styles/left_sidebar.css | 27 ++------------------- web/templates/filter_topics.hbs | 14 ++++------- 6 files changed, 79 insertions(+), 60 deletions(-) diff --git a/web/src/click_handlers.ts b/web/src/click_handlers.ts index 5b5e3a473f..b4439d68cd 100644 --- a/web/src/click_handlers.ts +++ b/web/src/click_handlers.ts @@ -838,7 +838,7 @@ export function initialize(): void { // LEFT SIDEBAR - $("body").on("click", "#clear_search_topic_button", topic_list.clear_topic_search); + $("body").on("click", ".filter-topics .input-button", topic_list.clear_topic_search); $(".streams_filter_icon").on("click", (e) => { e.stopPropagation(); diff --git a/web/src/inputs.ts b/web/src/inputs.ts index 786ae2f858..4b4b907180 100644 --- a/web/src/inputs.ts +++ b/web/src/inputs.ts @@ -1,6 +1,8 @@ import $ from "jquery"; -$("body").on("input", ".input-element", function (this: HTMLInputElement, _e: JQuery.Event) { +// We use the `input` tag in the selector to avoid conflicts with the pill containing +// counterpart, which uses a `contenteditable` div instead of an input element. +$("body").on("input", "input.input-element", function (this: HTMLInputElement, _e: JQuery.Event) { if (this.value.length === 0) { $(this).removeClass("input-element-nonempty"); } else { @@ -8,6 +10,25 @@ $("body").on("input", ".input-element", function (this: HTMLInputElement, _e: JQ } }); +$("body").on( + "input change", + ".has-input-pills .pill-container", + function (this: HTMLInputElement, _e: JQuery.Event) { + // We define another event handler for inputs with pill, similar to the one above. + // However, due to the way inputs with pill use a contenteditable div instead of an + // input element, we need to check the textContent of the pill container instead of + // the value of an input element. + // Here we need to listen to the `change` event in conjunction with the `input` event + // to handle the addition or removal of input pills. + const value = this.textContent?.trim() ?? ""; + if (value.length === 0) { + $(this).removeClass("input-element-nonempty"); + } else { + $(this).addClass("input-element-nonempty"); + } + }, +); + $("body").on( "click", ".filter-input .input-button", diff --git a/web/src/topic_list.ts b/web/src/topic_list.ts index 64caba306e..b9b3e9509c 100644 --- a/web/src/topic_list.ts +++ b/web/src/topic_list.ts @@ -374,26 +374,17 @@ export class LeftSidebarTopicListWidget extends TopicListWidget { export function clear_topic_search(e: JQuery.Event): void { e.stopPropagation(); + + search_pill_widget?.clear(true); + const $input = $("#topic_filter_query"); - if ($input.length > 0) { - $input.text(""); - $input.trigger("blur"); - - search_pill_widget?.clear(true); - update_clear_button(); - - // Since this changes the contents of the search input, we - // need to rerender the topic list. - const stream_ids = [...active_widgets.keys()]; - - const stream_id = stream_ids[0]; - assert(stream_id !== undefined); - const widget = active_widgets.get(stream_id); - assert(widget !== undefined); - const parent_widget = widget.get_parent(); - - rebuild_left_sidebar(parent_widget, stream_id); - } + // Since the `clear` function of the search_pill_widget + // takes care of clearing both the text content and the + // pills, we just need to trigger an input event on the + // contenteditable element to reset the topic list via + // the `input` event handler without having to manually + // manage the reset of the topic list. + $input.trigger("input"); } export function active_stream_id(): number | undefined { @@ -517,7 +508,7 @@ const filter_options = new Map([ export function update_clear_button(): void { const $filter_query = $("#topic_filter_query"); - const $clear_button = $("#clear_search_topic_button"); + const $clear_button = $(".filter-topics .input-button"); if (get_left_sidebar_topic_search_term() === "" && get_typeahead_search_term() === "") { $clear_button.css("visibility", "hidden"); // When we use backspace to clear the content of the search box, diff --git a/web/styles/inputs.css b/web/styles/inputs.css index 0e592d1fc0..594e001df4 100644 --- a/web/styles/inputs.css +++ b/web/styles/inputs.css @@ -60,6 +60,38 @@ var(--input-button-ending-offset) ); } + + /* Special styles for input with pills */ + &.has-input-pills .pill-container { + .input { + flex-grow: 1; + /* Override default values in web/styles/input_pill.css */ + padding: 0; + line-height: inherit; + + &:empty::before { + color: var(--color-text-placeholder); + content: attr(data-placeholder); + } + } + + .pill { + height: 1.25em; /* 20px at 16px/1em */ + } + + &:has(.input:hover) { + border-color: var(--color-border-input-hover); + } + + &:has(.input:focus) { + box-shadow: 0 0 5px var(--color-box-shadow-input-focus); + } + + &:has(.input:focus), + &.input-element-nonempty:has(.input) { + @extend .input-active-styles; + } + } } .input-icon { @@ -77,11 +109,15 @@ padding: 0.25em; /* 4px at 16px/1em */ } -.filter-input .input-element { +/* We use the `input` tag in the selector to avoid conflicts + with the pill containing counterpart, which uses a `contenteditable` + div instead of an input element, and thus doesn't support the + placeholder pseudo-classes. */ +.filter-input input.input-element { &:placeholder-shown { /* In case of filter inputs, when the input field - is empty, we hide the input button and adjust - the right padding to compensate for the same. */ + is empty, we hide the input button and adjust + the right padding to compensate for the same. */ padding-right: 0.5em; ~ .input-button { diff --git a/web/styles/left_sidebar.css b/web/styles/left_sidebar.css index 8056bb36f6..9a321a961d 100644 --- a/web/styles/left_sidebar.css +++ b/web/styles/left_sidebar.css @@ -1363,31 +1363,8 @@ li.top_left_scheduled_messages { } } -.topic-list-filter { - grid-area: filter-box; - padding-right: var(--line-height-sidebar-row-prominent); - box-shadow: none; - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; - gap: 0.125em; /* 2px at 16px em */ - background-color: var(--color-background-active-narrow-filter); - font-weight: 400; - - .input:empty::before { - color: var(--color-text-placeholder); - content: attr(data-placeholder); - } - - .input { - flex-grow: 1; - min-width: 0; - } -} - -#clear_search_topic_button { - visibility: hidden; - height: 2em; /* 32px at 16px/1em */ +.filter-topics { + font-weight: initial; } .searching-for-more-topics img { diff --git a/web/templates/filter_topics.hbs b/web/templates/filter_topics.hbs index 477732142d..97a3972605 100644 --- a/web/templates/filter_topics.hbs +++ b/web/templates/filter_topics.hbs @@ -1,13 +1,7 @@