Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/components/_index.sass
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
@import "enterprise_edition/banner_component"
@import "filter/filters_component"
@import "op_primer/border_box_list_component"
@import "op_primer/border_box_table_component"
@import "op_primer/full_page_prompt_component"
@import "op_primer/form_helpers"
Expand All @@ -12,7 +13,6 @@
@import "open_project/common/submenu_component"
@import "open_project/common/main_menu_toggle_component"
@import "open_project/common/work_package_card_list_component"
@import "open_project/common/work_package_card_list_component/header"
@import "open_project/common/work_package_card_component"
@import "portfolios/details_component"
@import "projects/row_component"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,25 +29,19 @@ See COPYRIGHT and LICENSE files for more details.

<%= render(Primer::Beta::BorderBox.new(**@system_arguments)) do |border_box| %>
<% if header? %>
<% border_box.with_header(id: header_id) do %>
<% border_box.with_header(**header.row_args) do %>
<%= header %>
<% end %>
<% end %>

<% if items.empty? %>
<% border_box.with_row(data: { empty_list_item: true }) do %>
<%= empty_state %>
<% end %>
<% else %>
<% items.each do |item| %>
<% border_box.with_row(**item.row_args) do %>
<%= render(item.card) %>
<% end %>
<% items.each do |item| %>
<% border_box.with_row(**item.row_args) do %>
<%= item %>
<% end %>
<% end %>

<% if footer? %>
<% border_box.with_row(scheme: :neutral) do %>
<% border_box.with_footer(**footer.footer_args) do %>
<%= footer %>
<% end %>
<% end %>
Expand Down
146 changes: 146 additions & 0 deletions app/components/op_primer/border_box_list_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
# 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
# Static BorderBox-backed list shell for callers that need ordered header,
# item, empty-item, and footer content without list behavior such as sorting
# or reordering.
class BorderBoxListComponent < ApplicationComponent
# @!parse
# # Adds a structured header rendered through the underlying BorderBox
# # header slot, with optional count, description, actions, and menu.
# #
# # @param title [String] header title.
# # @param count [Integer, nil] optional count badge.
# # @param list_id [String, nil] id of the collapsible BorderBox list body.
# # @param collapsed [Boolean] whether the collapsible header starts closed.
# # @param count_aria_label [String, nil] accessible label for the count badge.
# # @param system_arguments [Hash] forwarded to `Header`, whose `row_args`
# # are forwarded to `Primer::Beta::BorderBox#with_header`.
# def with_header(title:, **system_arguments, &block)
# end
renders_one :header, ->(**system_arguments) {
system_arguments[:list_id] ||= list_id

Header.new(**system_arguments)
}

# @!parse
# # Adds an arbitrary content row.
# #
# # @param component_klass [Class] class to use instead of the default
# # `Item`. The class must implement `row_args` for BorderBox row
# # arguments and render its own content.
# # @param system_arguments [Hash] forwarded to `component_klass` or
# # `Primer::Beta::BorderBox#with_row`.
# def with_item(component_klass: Item, **system_arguments, &block)
# end
#
# @!parse
# # Adds an empty-state content row with `data-empty-list-item="true"`.
# #
# # @param system_arguments [Hash] forwarded to `Primer::Beta::BorderBox#with_row`.
# def with_empty_item(**system_arguments, &block)
# end
renders_many :items, types: {
item: {
renders: ->(component_klass: Item, **system_arguments) {
component_klass.new(**system_arguments)
},
as: :item
},
empty_item: {
renders: ->(**system_arguments) {
EmptyItem.new(**system_arguments)
},
as: :empty_item
}
}

# @!parse
# # Gets the configured list items.
# #
# # This is intentionally public so wrapper components can delegate item
# # access while keeping underlying BorderBox rows as an internal detail.
# #
# # @return [Array<ViewComponent::Slot>]
# def items
# end

# @!parse
# # Adds a footer below the list body.
# #
# # When the parent BorderBox has a `list_id`, the footer receives a
# # stable default id derived from that list id unless an explicit id is
# # provided.
# #
# # @param system_arguments [Hash] forwarded to `Footer`, whose
# # `footer_args` are forwarded to `Primer::Beta::BorderBox#with_footer`.
# def with_footer(**system_arguments, &block)
# end
renders_one :footer, ->(**system_arguments) {
system_arguments[:id] ||= dom_target(list_id, :footer) if list_id

Footer.new(**system_arguments)
}

# @param system_arguments [Hash] forwarded to `Primer::Beta::BorderBox`.
def initialize(**system_arguments)
super()

@system_arguments = system_arguments
end

def before_render
content
apply_header_defaults!
end

def render?
header? || items.any? || footer?
end

private

def apply_header_defaults!
return unless header?

header.collapsible_id = [list_id, footer_id].compact.join(" ")
end

def list_id
@system_arguments[:list_id]
end

def footer_id
footer&.id
end
end
end
34 changes: 34 additions & 0 deletions app/components/op_primer/border_box_list_component.sass
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//-- 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.
//
// See COPYRIGHT and LICENSE files for more details.
//++

.op-border-box-list-header
display: grid
grid-template-columns: 1fr minmax(5rem, max-content) auto
grid-template-areas: "collapsible actions menu"
align-items: center

&--actions,
&--menu
margin-left: var(--stack-gap-normal)
align-self: flex-start
// Unfortunately, the invisible button style bites us here again.
margin-top: -6px

.op-border-box-list-item
display: grid
grid-template-columns: minmax(0, 1fr) auto
align-items: start
gap: var(--stack-gap-normal)

&--content
min-width: 0

&--menu
margin-top: -6px
Original file line number Diff line number Diff line change
Expand Up @@ -23,28 +23,33 @@
#
# 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.
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

module OpenProject
module Common
class WorkPackageCardListComponent
# Row bridge for caller-provided empty content.
class EmptyItem < ContentItem
include Primer::AttributesHelper
module OpPrimer
class BorderBoxListComponent
class EmptyItem < ApplicationComponent
include Primer::AttributesHelper

def row_args
system_arguments = @system_arguments.deep_dup
system_arguments[:data] = merge_data(
{ data: { empty_list_item: true } },
system_arguments
)
def initialize(**system_arguments)
super()

@system_arguments = system_arguments
end

def row_args
system_arguments = @system_arguments.deep_dup
system_arguments[:data] = merge_data(
{ data: { empty_list_item: true } },
system_arguments
end
)
system_arguments
end

def empty_item? = true
def call
content
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,35 +23,29 @@
#
# 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.
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

module OpenProject
module Common
class WorkPackageCardListComponent
# Item bridge for caller-provided content.
class ContentItem < ApplicationComponent
def initialize(**system_arguments)
super()
module OpPrimer
class BorderBoxListComponent
class Footer < ApplicationComponent
attr_reader :id

@system_arguments = system_arguments
end
def initialize(**system_arguments)
super()

def row_args
@system_arguments.deep_dup
end

def card
self
end
@id = system_arguments[:id]
@system_arguments = system_arguments
end

def empty_item? = false
def footer_args
@system_arguments.deep_dup
end

def call
content
end
def call
content
end
end
end
Expand Down
Loading
Loading