From dfeed5eb0716e784c27e5115de230593c4d9a7c3 Mon Sep 17 00:00:00 2001 From: Henriette Darge Date: Tue, 3 Sep 2024 14:08:25 +0200 Subject: [PATCH] Add arrows for the UnderlineNav of the split screen to navigate without a scrollbar. We actively hide the scrollbar to avoid the scrollbar overlapping the active element --- .../details/tab_component.html.erb | 39 ++++++++++++- .../work_packages/details/tab_component.sass | 3 + config/locales/en.yml | 2 + .../src/global_styles/primer/_overrides.sass | 9 +-- .../work-packages/details/tabs.controller.ts | 56 +++++++++++++++++++ 5 files changed, 99 insertions(+), 10 deletions(-) create mode 100644 frontend/src/stimulus/controllers/dynamic/work-packages/details/tabs.controller.ts diff --git a/app/components/work_packages/details/tab_component.html.erb b/app/components/work_packages/details/tab_component.html.erb index 6b99f5f2b57c..f684953bf5cd 100644 --- a/app/components/work_packages/details/tab_component.html.erb +++ b/app/components/work_packages/details/tab_component.html.erb @@ -1,12 +1,35 @@ <%= - flex_layout(classes: "op-work-package-details-tab-component") do |flex| + flex_layout(classes: "op-work-package-details-tab-component", data: { + "application-target": "dynamic", + controller: "work-packages--details--tabs" + }) do |flex| + flex.with_column(classes: "op-work-package-details-tab-component--action") do + render(Primer::Beta::IconButton.new(icon: :"chevron-left", + tag: :a, + scheme: :invisible, + data: { + action: "click->work-packages--details--tabs#scrollLeft" + }, + aria: { label: I18n.t(:label_scroll_left) })) + end + + flex.with_column(flex: 1, classes: "op-work-package-details-tab-component--tabs", test_selector: "wp-details-tab-component--tabs") do - render(Primer::Alpha::UnderlineNav.new(align: :left, label: "Tabs", classes: "op-primer-adjustment--UnderlineNav_spaciousLeft")) do |component| + render(Primer::Alpha::UnderlineNav.new(align: :left, + label: "Tabs", + data: { + "work-packages--details--tabs-target": "underlineNav", + })) do |component| menu_items.each do |node| component.with_tab(selected: @tab == node.name, href: helpers.url_for_with_params(**node.url), test_selector: "wp-details-tab-component--tab-#{node.name}", - data: { turbo: true, turbo_stream: true, turbo_action: "replace" } + data: { + turbo: true, + turbo_stream: true, + turbo_action: "replace", + "work-packages--details--tabs-target": @tab == node.name ? "activeElement" : "" + } ) do |c| c.with_text { t("js.work_packages.tabs.#{node.name}") } count = node.badge(work_package:).to_i @@ -16,6 +39,16 @@ end end + flex.with_column(classes: "op-work-package-details-tab-component--action") do + render(Primer::Beta::IconButton.new(icon: :"chevron-right", + tag: :a, + scheme: :invisible, + data: { + action: "click->work-packages--details--tabs#scrollRight" + }, + aria: { label: I18n.t(:label_scroll_right) })) + end + flex.with_column(classes: "op-work-package-details-tab-component--action") do render(Primer::Beta::IconButton.new(icon: :"screen-full", tag: :a, diff --git a/app/components/work_packages/details/tab_component.sass b/app/components/work_packages/details/tab_component.sass index 23c2a467bf4c..4a6fdeb48444 100644 --- a/app/components/work_packages/details/tab_component.sass +++ b/app/components/work_packages/details/tab_component.sass @@ -14,3 +14,6 @@ &:last-of-type padding-right: 10px + + &:first-of-type + padding-left: 5px diff --git a/config/locales/en.yml b/config/locales/en.yml index 93b889d88728..6aa13ce5dd9e 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -2484,6 +2484,8 @@ en: label_role_plural: "Roles" label_role_search: "Assign role to new members" label_scm: "SCM" + label_scroll_left: "Scroll left" + label_scroll_right: "Scroll right" label_search: "Search" label_search_by_name: "Search by name" label_send_information: "Send new credentials to the user" diff --git a/frontend/src/global_styles/primer/_overrides.sass b/frontend/src/global_styles/primer/_overrides.sass index e74b5c0e2690..db99d3eeaa4e 100644 --- a/frontend/src/global_styles/primer/_overrides.sass +++ b/frontend/src/global_styles/primer/_overrides.sass @@ -46,15 +46,10 @@ action-menu margin-left: 0 .UnderlineNav - @include styled-scroll-bar + @include no-visible-scroll-bar + scroll-behavior: smooth margin-bottom: 12px - &-body - margin-left: 12px - - &.op-primer-adjustment--UnderlineNav_spaciousLeft - padding-left: 8px - /* Remove margin-left: 2rem from Breadcrumbs */ #breadcrumb, page-header, diff --git a/frontend/src/stimulus/controllers/dynamic/work-packages/details/tabs.controller.ts b/frontend/src/stimulus/controllers/dynamic/work-packages/details/tabs.controller.ts new file mode 100644 index 000000000000..243e588b61a1 --- /dev/null +++ b/frontend/src/stimulus/controllers/dynamic/work-packages/details/tabs.controller.ts @@ -0,0 +1,56 @@ +/* + * -- 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. + * ++ + */ + +import { Controller } from '@hotwired/stimulus'; + +export default class TabsController extends Controller { + static targets = [ + 'underlineNav', + 'activeElement', + ]; + + declare readonly underlineNavTarget:HTMLElement; + + declare readonly activeElementTarget:HTMLElement; + + connect() { + if (this.activeElementTarget.parentElement) { + this.activeElementTarget.parentElement.scrollIntoView(); + } + } + + scrollLeft() { + this.underlineNavTarget.scrollBy(-100, 0); + } + + scrollRight() { + this.underlineNavTarget.scrollBy(100, 0); + } +}