Project

General

Profile

Feature #42684 » 0001-Add-sticky-issue-header.patch

Mizuki ISHIKAWA, 2025-05-09 06:03

View differences:

app/assets/stylesheets/application.css
680 680
div.issue .attribute.text_cf .value .wiki p:first-of-type {margin-top: 0;}
681 681
div.issue.overdue .due-date .value { color: #c22; }
682 682
body.controller-issues h2.inline-flex {padding-right: 0}
683
div#sticky-issue-header {
684
  display: none;
685
  position: fixed;
686
  top: 0;
687
  left: 0;
688
  right: 0;
689
  background-color: white;
690
  border-bottom: 1px solid #d0d7de;
691
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
692
  font-size: 0.750rem;
693
  align-items: center;
694
  z-index: 1000;
695
  padding: 10px 6px;
696
  border-radius: 0px;
697
}
698
div#sticky-issue-header.is-visible {
699
  display: flex;
700
}
701
div#sticky-issue-header .issue-heading {
702
  flex-shrink: 0;
703
  white-space: nowrap;
704
  margin-right: 6px;
705
}
706
div#sticky-issue-header .subject {
707
  font-weight: bold;
708
  overflow: hidden;
709
  text-overflow: ellipsis;
710
  white-space: nowrap;
711
  flex-grow: 1;
712
}
683 713

  
684 714
#issue_tree table.issues, #relations table.issues {border: 0;}
685 715
#issue_tree table.issues td, #relations table.issues td {border: 0;}
app/assets/stylesheets/responsive.css
848 848
    font-size: 1.1em;
849 849
    text-align: left;
850 850
  }
851

  
852
  /* Sticky issue header */
853
  /* When project-jump.drdn is visible in mobile layout, offset the sticky header by its height to prevent it from being hidden. */
854
  div#sticky-issue-header {
855
    top: 64px;
856
  }
851 857
}
852 858

  
853 859
@media all and (max-width: 599px) {
app/javascript/controllers/sticky_issue_header_controller.js
1
import { Controller } from "@hotwired/stimulus";
2

  
3
export default class extends Controller {
4
  static targets = ["original", "stickyHeader"];
5

  
6
  connect() {
7
    if (!this.originalTarget || !this.stickyHeaderTarget) return;
8

  
9
    this.observer = new IntersectionObserver(
10
      ([entry]) => {
11
        this.stickyHeaderTarget.classList.toggle("is-visible", !entry.isIntersecting);
12
      },
13
      { threshold: 0 }
14
    );
15

  
16
    this.observer.observe(this.originalTarget);
17
  }
18

  
19
  disconnect() {
20
    this.observer?.disconnect();
21
  }
22
}
app/views/issues/show.html.erb
37 37
    <%= assignee_avatar(@issue.assigned_to, :size => "22", :class => "gravatar-child") if @issue.assigned_to %>
38 38
  </div>
39 39

  
40
<div class="subject">
41
<%= render_issue_subject_with_tree(@issue) %>
40
<div data-controller="sticky-issue-header">
41
  <div class="subject" data-sticky-issue-header-target="original">
42
    <%= render_issue_subject_with_tree(@issue) %>
43
  </div>
44
  <div id="sticky-issue-header" data-sticky-issue-header-target="stickyHeader" class="issue">
45
    <span class="issue-heading"><%= issue_heading(@issue) %>:</span>
46
    <span class="subject"><%= @issue.subject %></span>
47
  </div>
42 48
</div>
49

  
43 50
        <p class="author">
44 51
        <%= authoring @issue.created_on, @issue.author %>.
45 52
        <% if @issue.created_on != @issue.updated_on %>
test/system/sticky_issue_header_test.rb
1
# frozen_string_literal: true
2

  
3
# Redmine - project management software
4
# Copyright (C) 2006-  Jean-Philippe Lang
5
#
6
# This program is free software; you can redistribute it and/or
7
# modify it under the terms of the GNU General Public License
8
# as published by the Free Software Foundation; either version 2
9
# of the License, or (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19
require_relative '../application_system_test_case'
20
class StickyIssueHeaderSystemTest < ApplicationSystemTestCase
21
  test "sticky issue header is hidden by default" do
22
    issue = Issue.find(1)
23
    visit issue_path(issue)
24

  
25
    assert_no_selector "#sticky-issue-header", text: issue.subject
26
  end
27

  
28
  test "sticky issue header appears on scroll" do
29
    issue = Issue.find(1)
30
    visit issue_path(issue)
31

  
32
    page.execute_script("window.scrollTo(0, 1000)")
33
    assert_selector "#sticky-issue-header.is-visible", text: issue.subject
34

  
35
    page.execute_script("window.scrollTo(0, 0)")
36
    assert_no_selector "#sticky-issue-header", text: issue.subject
37
  end
38
end
(7-7/7)