This commit is contained in:
Mir Bhatia
2026-05-06 10:53:57 +02:00
parent f49a425f98
commit 29ee672061
5 changed files with 129 additions and 0 deletions
@@ -0,0 +1,39 @@
Quick filters are lightweight controls that let users toggle a single filter without opening the full filter panel. Each click can navigate to a new URL with updated `filters` and `sortBy` params,
preserving any other active filters.
There are currently two variants:
* SegmentedComponent
* BooleanComponent
## SegmentedComponent
A general purpose segmented control that accepts any number of items via `with_item` slots.
Each item has a label and a filter value (`nil` means "no filter" and is useful for an "All" option).
<%= embed OpPrimer::QuickFilterPreview, :segmented, panels: %i[source] %>
Note: Clicking on buttons in the above preview will break it and redirect to the meetings index page.
### With an active filter or order overrides
When the query already has a matching filter value, the corresponding item is rendered as selected.
You can also pass `orders` to define the sort order per value.
<%= embed OpPrimer::QuickFilterPreview, :segmented_with_active_filter, panels: %i[source] %>
Note: Clicking on buttons in the above preview will break it and redirect to the meetings index page.
## BooleanComponent
A specialized subclass of `SegmentedComponent` for boolean (true/false) filters.
It always renders exactly two items using `t` and `f` as filter values.
Labels are provided via `true_label` and `false_label`.
<%= embed OpPrimer::QuickFilterPreview, :boolean, panels: %i[source] %>
Note: Clicking on buttons in the above preview will break it and redirect to the meetings index page.
## Parameters
* `BooleanComponent` requires `true_label` and `false_label`.
* `SegmentedComponent` accepts items via a block using `with_item(label:, value:)`.
@@ -0,0 +1,56 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
module OpPrimer
# @logical_path OpenProject/Primer
class QuickFilterPreview < Lookbook::Preview
def segmented
render_with_template(locals: { query: meeting_query })
end
def segmented_with_active_filter
query = meeting_query
query.where("time", "=", ["future"])
render_with_template(locals: { query: })
end
def boolean
query = meeting_query
query.where("type", "=", ["t"])
render_with_template(locals: { query: })
end
private
def meeting_query
Queries::Meetings::MeetingQuery.new(user: User.current)
end
end
end
@@ -0,0 +1,10 @@
<%= render(
OpPrimer::QuickFilter::BooleanComponent.new(
name: "Recurring",
query: query,
filter_key: :type,
true_label: "Recurring",
false_label: "One-time",
path_args: [:meetings]
)
) %>
@@ -0,0 +1,12 @@
<%= render(
OpPrimer::QuickFilter::SegmentedComponent.new(
name: "Meeting type",
query: query,
filter_key: :type,
path_args: [:meetings]
)
) do |component|
component.with_item(label: "All", value: nil)
component.with_item(label: "One-time", value: "f")
component.with_item(label: "Recurring", value: "t")
end %>
@@ -0,0 +1,12 @@
<%= render(
OpPrimer::QuickFilter::SegmentedComponent.new(
name: "Time",
query: query,
filter_key: :time,
orders: { "future" => { start_time: :asc }, "past" => { start_time: :desc } },
path_args: [:meetings]
)
) do |component|
component.with_item(label: "Upcoming", value: "future")
component.with_item(label: "Past", value: "past")
end %>