mirror of
https://github.com/opf/openproject.git
synced 2026-06-14 03:30:14 +00:00
Handle color modes in status button by passing __hl classes
This commit is contained in:
@@ -14,17 +14,8 @@
|
||||
item.with_leading_visual_icon(icon: option.icon) if option.icon
|
||||
item.with_description.with_content(option.description) if option.description
|
||||
|
||||
flex_layout(align_items: :center) do |flex|
|
||||
if option.icon.nil? && option.color
|
||||
flex.with_column(mr: 1) do
|
||||
helpers.icon_for_color(option.color, style: "display: block")
|
||||
end
|
||||
end
|
||||
|
||||
flex.with_column do
|
||||
render(Primer::Beta::Text.new) { option.name }
|
||||
end
|
||||
end
|
||||
classes = option.colored? ? highlight_class_name(option, :inline) : ""
|
||||
render(Primer::Beta::Text.new(classes:)) { option.name }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
module OpPrimer
|
||||
class StatusButtonComponent < ApplicationComponent
|
||||
include OpPrimer::ComponentHelpers
|
||||
include Primer::ClassNameHelper
|
||||
|
||||
def initialize(current_status:, items:, readonly: false, disabled: false, button_arguments: {}, menu_arguments: {})
|
||||
super
|
||||
@@ -67,12 +68,21 @@ module OpPrimer
|
||||
|
||||
{
|
||||
title:,
|
||||
classes: class_names(@button_arguments[:classes], highlight_class_name(@current_status, :background)),
|
||||
disabled: disabled?,
|
||||
aria: {
|
||||
label: title
|
||||
},
|
||||
style: @current_status.color&.color_styles_css
|
||||
}
|
||||
}.compact.deep_merge(@button_arguments)
|
||||
end
|
||||
|
||||
def highlight_class_name(status, style)
|
||||
case style
|
||||
when :inline
|
||||
helpers.hl_inline_class(status.color_namespace, status.color_ref)
|
||||
when :background
|
||||
helpers.hl_background_class(status.color_namespace, status.color_ref)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -30,14 +30,20 @@
|
||||
|
||||
module OpPrimer
|
||||
class StatusButtonOption # rubocop:disable OpenProject/AddPreviewForViewComponent
|
||||
attr_reader :name, :color, :icon, :item_arguments, :description
|
||||
attr_reader :name, :icon, :item_arguments, :description, :color_namespace, :color_ref
|
||||
|
||||
def initialize(name:, color: nil, icon: nil, description: nil, **item_arguments)
|
||||
def initialize(name:, color_ref: nil, color_namespace: nil, icon: nil, description: nil, **item_arguments)
|
||||
@name = name
|
||||
@color = color
|
||||
@icon = icon
|
||||
@description = description
|
||||
@item_arguments = item_arguments
|
||||
|
||||
@color_ref = color_ref
|
||||
@color_namespace = color_namespace
|
||||
end
|
||||
|
||||
def colored?
|
||||
!icon && color_ref && color_namespace
|
||||
end
|
||||
|
||||
def to_s
|
||||
|
||||
@@ -1,19 +1,40 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class WorkPackages::StatusButtonComponent < OpPrimer::StatusButtonComponent
|
||||
include Primer::ClassNameHelper
|
||||
#-- 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.
|
||||
#++
|
||||
|
||||
class WorkPackages::StatusButtonComponent < OpPrimer::StatusButtonComponent
|
||||
attr_reader :work_package, :user
|
||||
|
||||
def initialize(work_package:, user:, readonly: false, button_arguments: {}, menu_arguments: {})
|
||||
@work_package = work_package
|
||||
@user = user
|
||||
|
||||
button_arguments[:classes] = class_names(
|
||||
button_arguments[:classes],
|
||||
"__hl_background_status_#{work_package.status.id}"
|
||||
)
|
||||
|
||||
super(
|
||||
current_status: map_status(work_package.status),
|
||||
items: available_statuses,
|
||||
@@ -40,6 +61,6 @@ class WorkPackages::StatusButtonComponent < OpPrimer::StatusButtonComponent
|
||||
|
||||
def map_status(status)
|
||||
icon = status.is_readonly? ? :lock : nil
|
||||
OpPrimer::StatusButtonOption.new(name: status.name, color: status.color, icon:)
|
||||
OpPrimer::StatusButtonOption.new(name: status.name, color_namespace: "status", color_ref: status.id, icon:)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
#-- copyright
|
||||
# OpenProject is an open source project management software.
|
||||
# Copyright (C) the OpenProject GmbH
|
||||
@@ -59,31 +61,37 @@ module ColorsHelper
|
||||
#
|
||||
# Styles to display the color of attributes (type, status etc.) for example in the WP view
|
||||
##
|
||||
def resource_color_css(name, scope, inline_foreground: false)
|
||||
def resources_scope_color_css(name, scope, inline_foreground: false)
|
||||
scope.includes(:color).find_each do |entry|
|
||||
color = entry.color
|
||||
|
||||
if color.nil?
|
||||
concat ".#{hl_inline_class(name, entry)}::before { display: none }\n"
|
||||
next
|
||||
end
|
||||
|
||||
if inline_foreground
|
||||
set_foreground_colors_for(class_name: ".#{hl_inline_class(name, entry)}", color:)
|
||||
else
|
||||
set_background_colors_for(class_name: ".#{hl_inline_class(name, entry)}::before", color:)
|
||||
end
|
||||
|
||||
set_background_colors_for(class_name: ".#{hl_background_class(name, entry)}", color:)
|
||||
resource_color_css(name, entry, inline_foreground: inline_foreground)
|
||||
end
|
||||
end
|
||||
|
||||
def resource_color_css(name, entry, inline_foreground: false)
|
||||
color = entry.color
|
||||
|
||||
if color.nil?
|
||||
concat ".#{hl_inline_class(name, entry)}::before { display: none }\n"
|
||||
return
|
||||
end
|
||||
|
||||
if inline_foreground
|
||||
set_foreground_colors_for(class_name: ".#{hl_inline_class(name, entry)}", color:)
|
||||
else
|
||||
set_background_colors_for(class_name: ".#{hl_inline_class(name, entry)}::before", color:)
|
||||
end
|
||||
|
||||
set_background_colors_for(class_name: ".#{hl_background_class(name, entry)}", color:)
|
||||
end
|
||||
|
||||
def hl_inline_class(name, model)
|
||||
"__hl_inline_#{name}_#{model.id}"
|
||||
id = model.respond_to?(:id) ? model.id : model
|
||||
"__hl_inline_#{name}_#{id}"
|
||||
end
|
||||
|
||||
def hl_background_class(name, model)
|
||||
"__hl_background_#{name}_#{model.id}"
|
||||
id = model.respond_to?(:id) ? model.id : model
|
||||
"__hl_background_#{name}_#{id}"
|
||||
end
|
||||
|
||||
def icon_for_color(color, options = {})
|
||||
@@ -170,11 +178,12 @@ module ColorsHelper
|
||||
background: rgb(var(--color-r), var(--color-g), var(--color-b)) !important;"
|
||||
mode = User.current.pref.theme
|
||||
|
||||
style += if mode == "light_high_contrast"
|
||||
"border: 1px solid hsla(var(--color-h), calc(var(--color-s) * 1%), calc((var(--color-l) - 75) * 1%), 1) !important;"
|
||||
else
|
||||
"border: 1px solid hsl(var(--color-h), calc(var(--color-s) * 1%), calc((var(--color-l) - 15) * 1%)) !important;"
|
||||
end
|
||||
style +=
|
||||
if mode == "light_high_contrast"
|
||||
"border: 1px solid hsla(var(--color-h), calc(var(--color-s) * 1%), calc((var(--color-l) - 75) * 1%), 1) !important;"
|
||||
else
|
||||
"border: 1px solid hsl(var(--color-h), calc(var(--color-s) * 1%), calc((var(--color-l) - 15) * 1%)) !important;"
|
||||
end
|
||||
|
||||
style
|
||||
end
|
||||
@@ -192,5 +201,6 @@ module ColorsHelper
|
||||
"color: hsla(var(--color-h), calc(var(--color-s) * 1%), calc((var(--color-l) - (var(--color-l) * 0.22)) * 1%), 1) !important;"
|
||||
end
|
||||
end
|
||||
|
||||
# rubocop:enable Layout/LineLength
|
||||
end
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
<%# Highlightable resources %>
|
||||
<%= resource_color_css("status", ::Status) %>
|
||||
<%= resource_color_css("priority", ::IssuePriority) %>
|
||||
<%= resource_color_css("type", ::Type, inline_foreground: true) %>
|
||||
<%= resources_scope_color_css("status", ::Status) %>
|
||||
<%= resources_scope_color_css("priority", ::IssuePriority) %>
|
||||
<%= resources_scope_color_css("type", ::Type, inline_foreground: true) %>
|
||||
<%# Color coded icons %>
|
||||
<%= resource_color_css("life_cycle_step_definition", Project::LifeCycleStepDefinition, inline_foreground: true) %>
|
||||
<%= resources_scope_color_css("life_cycle_step_definition", Project::LifeCycleStepDefinition, inline_foreground: true) %>
|
||||
|
||||
<% Meetings::Statuses::AVAILABLE.each do |meeting_status| %>
|
||||
<%= resource_color_css("meeting_status", meeting_status) %>
|
||||
<% end %>
|
||||
|
||||
<%= color_css %>
|
||||
|
||||
|
||||
@@ -10,21 +10,26 @@ module OpPrimer
|
||||
# @param size [Symbol] select [small, medium, large]
|
||||
def playground(readonly: false, disabled: false, size: :medium)
|
||||
status = OpPrimer::StatusButtonOption.new(name: "Open",
|
||||
color: FactoryBot.build(:color_maroon),
|
||||
tag: :a,
|
||||
color_ref: :open,
|
||||
color_namespace: :meeting_status,
|
||||
href: "/some/test")
|
||||
items = [
|
||||
status,
|
||||
OpPrimer::StatusButtonOption.new(name: "Closed",
|
||||
color: FactoryBot.build(:color_green),
|
||||
tag: :a,
|
||||
color_ref: :closed,
|
||||
color_namespace: :meeting_status,
|
||||
href: "/some/other/action")
|
||||
]
|
||||
component = OpPrimer::StatusButtonComponent.new(current_status: status,
|
||||
items:,
|
||||
readonly:,
|
||||
disabled:,
|
||||
button_arguments: { title: "Edit", size: })
|
||||
button_arguments: {
|
||||
title: "Edit",
|
||||
size:
|
||||
})
|
||||
|
||||
render(component)
|
||||
end
|
||||
@@ -32,11 +37,17 @@ module OpPrimer
|
||||
# See the [component documentation](/lookbook/pages/components/status_button) for more details.
|
||||
# @display min_height 200px
|
||||
def with_icon(size: :medium)
|
||||
status = OpPrimer::StatusButtonOption.new(name: "Open", icon: :unlock, color: Color.new(hexcode: "#FF0000"))
|
||||
status = OpPrimer::StatusButtonOption.new(name: "Open",
|
||||
color_ref: :open,
|
||||
color_namespace: :meeting_status,
|
||||
icon: :unlock)
|
||||
|
||||
items = [
|
||||
status,
|
||||
OpPrimer::StatusButtonOption.new(name: "Closed", icon: :lock, color: Color.new(hexcode: "#00FF00"))
|
||||
OpPrimer::StatusButtonOption.new(name: "Closed",
|
||||
color_ref: :closed,
|
||||
color_namespace: :meeting_status,
|
||||
icon: :lock)
|
||||
]
|
||||
|
||||
component = OpPrimer::StatusButtonComponent.new(current_status: status,
|
||||
@@ -52,15 +63,13 @@ module OpPrimer
|
||||
def with_description(size: :medium)
|
||||
status = OpPrimer::StatusButtonOption.new(name: "Open",
|
||||
icon: :unlock,
|
||||
description: "The status is open",
|
||||
color: Color.new(hexcode: "#FF0000"))
|
||||
description: "The status is open")
|
||||
|
||||
items = [
|
||||
status,
|
||||
OpPrimer::StatusButtonOption.new(name: "Closed",
|
||||
icon: :lock,
|
||||
description: "The status is closed",
|
||||
color: Color.new(hexcode: "#00FF00"))
|
||||
description: "The status is closed")
|
||||
]
|
||||
|
||||
component = OpPrimer::StatusButtonComponent.new(current_status: status,
|
||||
|
||||
@@ -43,12 +43,19 @@ module Meetings
|
||||
end
|
||||
|
||||
def call
|
||||
render(OpPrimer::StatusButtonComponent.new(current_status: current_status,
|
||||
items: [open_status, in_progress_status, closed_status],
|
||||
readonly: !edit_enabled?,
|
||||
disabled: !edit_enabled?,
|
||||
button_arguments: { title: t("label_meeting_state"), size: @size },
|
||||
menu_arguments: { size: :small }))
|
||||
render(
|
||||
OpPrimer::StatusButtonComponent.new(
|
||||
current_status: current_status,
|
||||
items: [open_status, in_progress_status, closed_status],
|
||||
readonly: !edit_enabled?,
|
||||
disabled: !edit_enabled?,
|
||||
button_arguments: {
|
||||
title: t("label_meeting_state"),
|
||||
size: @size
|
||||
},
|
||||
menu_arguments: { size: :small }
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
@@ -70,7 +77,8 @@ module Meetings
|
||||
|
||||
def open_status
|
||||
OpPrimer::StatusButtonOption.new(name: t("label_meeting_state_open"),
|
||||
color: Color.new(hexcode: "#1F883D"),
|
||||
color_ref: Meetings::Statuses::OPEN.id,
|
||||
color_namespace: :meeting_status,
|
||||
icon: :"issue-opened",
|
||||
tag: :a,
|
||||
description: t("text_meeting_open_dropdown_description"),
|
||||
@@ -82,7 +90,8 @@ module Meetings
|
||||
|
||||
def in_progress_status
|
||||
OpPrimer::StatusButtonOption.new(name: t("label_meeting_state_in_progress"),
|
||||
color: Color.new(hexcode: "#9A6700"),
|
||||
color_ref: Meetings::Statuses::IN_PROGRESS.id,
|
||||
color_namespace: :meeting_status,
|
||||
icon: :play,
|
||||
tag: :a,
|
||||
description: t("text_meeting_in_progress_dropdown_description"),
|
||||
@@ -94,7 +103,8 @@ module Meetings
|
||||
|
||||
def closed_status
|
||||
OpPrimer::StatusButtonOption.new(name: t("label_meeting_state_closed"),
|
||||
color: Color.new(hexcode: "#6E7781 "),
|
||||
color_ref: Meetings::Statuses::CLOSED.id,
|
||||
color_namespace: :meeting_status,
|
||||
icon: :"issue-closed",
|
||||
tag: :a,
|
||||
description: t("text_meeting_closed_dropdown_description"),
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
# 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 Meetings
|
||||
module Statuses
|
||||
RECORD = Struct.new(:id, :color, keyword_init: true)
|
||||
|
||||
OPEN = RECORD.new(id: "open", color: Color.new(hexcode: "#1F883D"))
|
||||
IN_PROGRESS = RECORD.new(id: "in_progress", color: Color.new(hexcode: "#9A6700"))
|
||||
CLOSED = RECORD.new(id: "closed", color: Color.new(hexcode: "#565c63"))
|
||||
|
||||
AVAILABLE = [
|
||||
OPEN,
|
||||
IN_PROGRESS,
|
||||
CLOSED
|
||||
].freeze
|
||||
end
|
||||
end
|
||||
@@ -85,36 +85,6 @@ RSpec.describe OpPrimer::StatusButtonComponent, type: :component do
|
||||
end
|
||||
end
|
||||
|
||||
context "when rendering with color status" do
|
||||
let(:current_status) do
|
||||
OpPrimer::StatusButtonOption.new(
|
||||
name: "High Priority",
|
||||
icon: nil,
|
||||
color: Color.new(hexcode: "#FF0000")
|
||||
)
|
||||
end
|
||||
|
||||
let(:items) do
|
||||
[
|
||||
OpPrimer::StatusButtonOption.new(
|
||||
name: "Low Priority",
|
||||
icon: nil,
|
||||
color: Color.new(hexcode: "#00FF00")
|
||||
)
|
||||
]
|
||||
end
|
||||
|
||||
it "renders the status with color styles" do
|
||||
render_inline(component)
|
||||
|
||||
expect(page).to have_css("[style*='background-color: #FF0000']")
|
||||
expect(page).to have_text("High Priority")
|
||||
|
||||
expect(page).to have_css("[style*='background-color: #00FF00']", visible: :all)
|
||||
expect(page).to have_text("Low Priority")
|
||||
end
|
||||
end
|
||||
|
||||
context "when readonly" do
|
||||
let(:readonly) { true }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user