Merge pull request #1210 from basecamp/collections-filter

Collections filtering
This commit is contained in:
Jason Zimdars
2025-10-02 15:39:49 -05:00
committed by GitHub
19 changed files with 133 additions and 50 deletions
+1 -1
View File
@@ -1 +1 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m3 5h8v2h-8zm10 0h8v2h-8zm-10 4h8v2h-8zm10 0h8v2h-8zm-10 4h8v2h-8zm10 0h8v2h-8zm-10 4h8v2h-8z"/></svg>
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m21.8 0h-19.6c-1.2 0-2.2 1-2.2 2.2v19.5c0 1.2 1 2.2 2.2 2.2h19.5c1.2 0 2.2-1 2.2-2.2v-19.5c0-1.2-1-2.2-2.2-2.2zm-10.8 19h-8v-2h8zm0-4h-8v-2h8zm0-4h-8v-2h8zm0-4h-8v-2h8zm10 8h-8v-2h8zm0-4h-8v-2h8zm0-4h-8v-2h8z"/></svg>

Before

Width:  |  Height:  |  Size: 171 B

After

Width:  |  Height:  |  Size: 286 B

+11
View File
@@ -97,6 +97,17 @@
position: absolute;
}
.events__filter-select {
font-weight: inherit;
text-decoration: underline;
@media (any-hover: hover) {
&:hover {
--btn-color: var(--color-link);
}
}
}
.events__day {
position: relative;
+2 -2
View File
@@ -54,8 +54,8 @@ module PaginationHelper
def day_timeline_pagination_link(day_timeline, filter)
if day_timeline.next_day
link_to "Load more...", events_days_path(day: day_timeline.next_day.strftime("%Y-%m-%d"), **filter.as_params),
data: { frame: day_timeline_pagination_frame_id_for(day_timeline.next_day), pagination_target: "paginationLink" }
link_to "Load more", events_days_path(day: day_timeline.next_day.strftime("%Y-%m-%d"), **filter.as_params),
class: "txt-reversed", data: { frame: day_timeline_pagination_frame_id_for(day_timeline.next_day), pagination_target: "paginationLink" }
end
end
@@ -5,7 +5,7 @@ import { post } from "@rails/request.js"
export default class extends Controller {
static classes = ["filtersSet"]
static targets = ["field", "form"]
static values = { refreshUrl: String, noFilteringUrl: String }
static values = { refreshUrl: String, noFilteringUrl: String, cardsUrl: String }
initialize() {
this.debouncedToggle = debounce(this.#toggle.bind(this), 50)
@@ -31,6 +31,12 @@ export default class extends Controller {
this.debouncedToggle()
}
submitToGenericCardsView() {
this.formTarget.action = this.cardsUrlValue
this.formTarget.dataset.turboFrame = "top"
this.formTarget.requestSubmit()
}
#toggle() {
this.element.classList.toggle(this.filtersSetClass, this.#hasFiltersSet)
}
@@ -82,15 +82,14 @@ export default class extends Controller {
#clearHiddenFields() {
this.element.querySelectorAll('input[type="hidden"]').forEach(field => {
if (field !== this.hiddenField) {
field.remove()
}
field.remove()
})
}
#addHiddenFields() {
this.#selectedValues().forEach(value => {
const [field] = this.hiddenFieldTemplateTarget.content.cloneNode(true).children
const [ field ] = this.hiddenFieldTemplateTarget.content.cloneNode(true).children
field.removeAttribute("id")
field.value = value
this.element.appendChild(field)
})
-1
View File
@@ -22,7 +22,6 @@ class Card < ApplicationRecord
case index
when "stalled" then stalled
when "postponing_soon" then postponing_soon
when "falling_back_soon" then falling_back_soon
when "closed" then closed.recently_closed_first
when "golden" then golden
when "draft" then drafted
+1 -1
View File
@@ -1,7 +1,7 @@
module Filter::Fields
extend ActiveSupport::Concern
INDEXES = %w[ all stalled postponing_soon falling_back_soon golden draft ]
INDEXES = %w[ all stalled postponing_soon golden draft ]
SORTED_BY = %w[ newest oldest latest ]
delegate :default_value?, to: :class
+4
View File
@@ -67,6 +67,10 @@ class User::Filtering
filter.closers.any?
end
def show_collections?
filter.collections.any?
end
def enable_collection_filtering(&block)
@collection_filtering_route_resolver = block
end
+6 -4
View File
@@ -1,9 +1,11 @@
<%= link_to event.notifiable_target,
class: "event event--#{ event.action } #{ "golden-effect" if event.card.golden? } center-block flex flex-column full-width align-start justify-start position-relative",
style: "--card-color: #{ card.closed? ? "var(--color-card-complete)" : card.color };", target: "_top",
data: { related_element_target: "related",
related_element_group_value: card.id,
action: "mouseover->related-element#highlight mouseout->related-element#unhighlight" } do %>
style: "--card-color: #{ card.closed? ? "var(--color-card-complete)" : card.color };",
target: "_top",
data: {
related_element_target: "related",
related_element_group_value: card.id,
action: "mouseover->related-element#highlight mouseout->related-element#unhighlight" } do %>
<div class="card__header flex align-start gap full-width">
<h4 class="card__collection flex-inline align-start">
<span class="card__id"><%= card.id %></span>
+3 -4
View File
@@ -7,10 +7,9 @@
<%= render "filters/menu", user_filtering: @user_filtering %>
<%= render "events/index/add_card_button", user_filtering: @user_filtering %>
<h1 class="header__title overflow-ellipsis">
<div class="overflow-ellipsis">
Activity in <%= @user_filtering.selected_collections_label %>
</div>
<h1 class="header__title">
<span>Activity in</span>
<%= render "events/index/filter", user_filtering: @user_filtering %>
</h1>
<% end %>
+43
View File
@@ -0,0 +1,43 @@
<% filter = user_filtering.filter %>
<%= form_with url: events_path,
method: :get, class: "flex-inline position-relative",
data: { controller: "form", turbo_frame: "cards_container", turbo_action: "advance" } do |form| %>
<%= tag.div class: "flex-inline position-relative quick-filter",
data: {
controller: "dialog multi-selection-combobox",
action: "keydown.esc->dialog#close click@document->dialog#closeOnClickOutside",
filter_show: user_filtering.show_collections?,
multi_selection_combobox_no_selection_label_value: user_filtering.selected_collections_label } do %>
<button type="button" class="btn borderless btn--plain events__filter-select" data-action="click->dialog#toggle:stop">
<span class="overflow-ellipsis" data-multi-selection-combobox-target="label">
</span>
</button>
<template data-multi-selection-combobox-target="hiddenFieldTemplate">
<%= hidden_field_tag "collection_ids[]", nil, data: { filter_settings_target: "field" } %>
</template>
<%= filter_dialog "Collection…" do %>
<strong class="popup__title">Collection…</strong>
<% if user_filtering.collections.many? %>
<%= text_field_tag nil, nil, placeholder: "Filter…", class: "input input--transparent txt-small font-weight-normal", autofocus: true,
type: "search", autocorrect: "off", autocomplete: "off", data: { "1p-ignore": "true", filter_target: "input", action: "input->filter#filter" } %>
<% end %>
<ul class="popup__list" data-filter-target="list" role="listbox">
<% user_filtering.collections.each do |collection| %>
<%= tag.li class: "popup__item", data: {
filter_target: "item", navigable_list_target: "item", multi_selection_combobox_target: "item", multi_selection_combobox_value: collection.id, multi_selection_combobox_label: collection.name },
role: "checkbox", aria: { checked: filter.collections.include?(collection) } do %>
<button type="button" class="btn popup__btn" data-action="dialog#close multi-selection-combobox#change filter-settings#change form#submit">
<span class="overflow-ellipsis flex-item-grow"><%= collection.name %></span>
<%= icon_tag "check", class: "checked flex-item-justify-end", "aria-hidden": true %>
</button>
<% end %>
<% end %>
</ul>
<% end %>
<% end %>
<% end %>
+3 -5
View File
@@ -6,8 +6,9 @@
toggle_class_toggle_class: "filters--expanded",
filter_settings_filters_set_class: "filters--has-filters-set",
filter_settings_no_filtering_url_value: no_filtering_url,
turbo_permanent: true,
filter_settings_refresh_url_value: settings_refresh_path } do %>
filter_settings_refresh_url_value: settings_refresh_path,
filter_settings_cards_url_value: cards_path,
turbo_permanent: true } do %>
<%= form_with url: filter_url, method: :get, class: "", data: {
controller: "form",
turbo_frame: "cards_container",
@@ -15,9 +16,6 @@
action: "turbo:submit-end->filter-settings#resetIfNoFiltering",
turbo_action: "advance" } do |form| %>
<%= hidden_field_tag :expand_all, true, disabled: !user_filtering.expanded?, data: { toggle_enable_target: "element"} %>
<% user_filtering.filter.collections.each do |collection| %>
<%= filter_hidden_field_tag "collection_ids[]", collection.id %>
<% end %>
<%= yield form if block_given? %>
@@ -1,5 +1,5 @@
<% if platform.desktop? %>
<footer class="popup__footer">
Press <kbd class="kbd">J</kbd> anytime to open this, <kbd class="kbd">esc</kbd> to close, <kbd class="kbd">↑</kbd><kbd class="kbd">↓</kbd> to move, <kbd class="kbd">enter</kbd> to navigate, <kbd class="kbd">SHIFT+ENTER</kbd> to select.
Press <kbd class="kbd">J</kbd> anytime to open this, <kbd class="kbd">esc</kbd> to close, <kbd class="kbd">↑</kbd><kbd class="kbd">↓</kbd> to move, <kbd class="kbd">enter</kbd> to navigate.
</footer>
<% end %>
@@ -2,15 +2,8 @@
<% if Current.user.collections.many? %>
<li class="popup__item" data-filter-target="item" data-navigable-list-target="item" id="filter-collection-all">
<%= link_to clear_url_path, class: "popup__radio btn" do %>
<%= check_box_tag "filter_collections", nil, user_filtering.collections.blank?, class: "form-checkbox" %>
<span class="for-screen-reader">
See everything in all collections
</span>
<%= icon_tag "check", size: 18, class: "checked" %>
<% end %>
<%= link_to clear_url_path, class: "popup__btn btn" do %>
<%= icon_tag "collection", class: "popup__icon" %>
<%= link_to cards_path, class: "popup__btn btn" do %>
<span class="overflow-ellipsis">All collections</span>
<% end %>
</li>
@@ -1,17 +1,5 @@
<li class="popup__item" data-filter-target="item" data-navigable-list-target="item" id="filter-collection-<%= collection.id %>">
<label class="popup__radio btn">
<%= form.check_box "collection_ids[]", {
checked: user_filtering.filter.collections.include?(collection),
data: { action: "change->form#submit" },
include_hidden: false,
}, collection.id %>
<span class="for-screen-reader">
Toggle filter for <%= collection.name %>
</span>
<%= icon_tag "check", size: 18, class: "checked" %>
</label>
<%= icon_tag "collection", class: "popup__icon" %>
<%= link_to collection_path(collection), class: "popup__btn btn" do %>
<span class="overflow-ellipsis"><%= collection.name %></span>
<% end %>
@@ -2,8 +2,8 @@
<%= icon_tag "bookmark", class: "popup__icon" %>
<%= link_to cards_path(filter_id: filter.id), class: "popup__btn btn" do %>
<div class="txt-tight-lines min-width txt-small overflow-ellipsis">
<div><%= filter.collections_label %></div>
<div><%= filter.summary %></div>
<div><strong><%= filter.collections_label %></strong></div>
<div class="txt-capitalize"><%= filter.summary %></div>
</div>
<% end %>
</li>
@@ -0,0 +1,40 @@
<% filter = user_filtering.filter %>
<%= tag.div class: "quick-filter position-relative",
data: {
controller: "dialog multi-selection-combobox",
action: "keydown.esc->dialog#close click@document->dialog#closeOnClickOutside",
filter_show: user_filtering.show_collections?,
multi_selection_combobox_no_selection_label_value: "Collection…",
multi_selection_combobox_label_prefix_value: "" } do %>
<button type="button" class="btn input input--select flex-inline txt-x-small" data-action="click->dialog#toggle:stop">
<span class="overflow-ellipsis" data-multi-selection-combobox-target="label">
</span>
</button>
<template data-multi-selection-combobox-target="hiddenFieldTemplate">
<%= hidden_field_tag "collection_ids[]", nil, data: { filter_settings_target: "field" } %>
</template>
<%= filter_dialog "Collection…" do %>
<strong class="popup__title">Collection…</strong>
<% if user_filtering.collections.many? %>
<%= text_field_tag nil, nil, placeholder: "Filter…", class: "input input--transparent txt-small", autofocus: true,
type: "search", autocorrect: "off", autocomplete: "off", data: { "1p-ignore": "true", filter_target: "input", action: "input->filter#filter" } %>
<% end %>
<ul class="popup__list" data-filter-target="list" role="listbox">
<% user_filtering.collections.each do |collection| %>
<%= tag.li class: "popup__item", data: {
filter_target: "item", navigable_list_target: "item", multi_selection_combobox_target: "item", multi_selection_combobox_value: collection.id, multi_selection_combobox_label: collection.name },
role: "checkbox", aria: { checked: filter.collections.include?(collection) } do %>
<button type="button" class="btn popup__btn" data-action="dialog#close multi-selection-combobox#change filter-settings#change filter-settings#submitToGenericCardsView:stop">
<span class="overflow-ellipsis flex-item-grow"><%= collection.name %></span>
<%= icon_tag "check", class: "checked flex-item-justify-end", "aria-hidden": true %>
</button>
<% end %>
<% end %>
</ul>
<% end %>
<% end %>
@@ -1,3 +1,4 @@
<%= render "filters/settings/collections", user_filtering: user_filtering %>
<%= render "filters/settings/sorted_by", user_filtering: user_filtering %>
<%= render "filters/settings/indexed_by", user_filtering: user_filtering %>
<%= render "filters/settings/tags", user_filtering: user_filtering %>
+2 -2
View File
@@ -3,8 +3,8 @@
<%= render "events/day", day_timeline: day_timeline %>
<% if day_timeline.next_day %>
<%= link_to "Load more...", user_path(user, day: day_timeline.next_day.strftime("%Y-%m-%d"), **filter.as_params),
data: { frame: day_timeline_pagination_frame_id_for(day_timeline.next_day), pagination_target: "paginationLink" } %>
<%= link_to "Load more", user_path(user, day: day_timeline.next_day.strftime("%Y-%m-%d"), **filter.as_params),
class: "txt-reversed", data: { frame: day_timeline_pagination_frame_id_for(day_timeline.next_day), pagination_target: "paginationLink" } %>
<% end %>
<% end %>
</div>