Feature #42684 » 0001-Add-sticky-issue-header.patch
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 |
- « Previous
- 1
- …
- 5
- 6
- 7
- Next »