Merge pull request #1502 from basecamp/sticky-column-headers-using-fixed
Sticky column headers using fixed
This commit is contained in:
@@ -63,20 +63,9 @@
|
||||
.cards {
|
||||
--column-color: color-mix(in srgb, var(--card-color) 15%, var(--color-canvas));
|
||||
|
||||
border-radius: calc(var(--column-width-collapsed) / 2);
|
||||
inline-size: var(--column-width-expanded);
|
||||
position: relative;
|
||||
scroll-snap-align: start;
|
||||
translate: 0;
|
||||
transition: translate var(--column-transition-duration) var(--ease-out-overshoot-subtle);
|
||||
|
||||
.no-transitions & {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
&:not(.is-collapsed) {
|
||||
padding-inline: var(--column-padding-expanded);
|
||||
}
|
||||
|
||||
&.is-collapsed {
|
||||
inline-size: var(--column-width-collapsed);
|
||||
@@ -85,12 +74,41 @@
|
||||
.card {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.cards--considering) {
|
||||
translate: 0 var(--column-width-collapsed);
|
||||
&.drag-and-drop__hover-container {
|
||||
--dnd-bg-color: transparent;
|
||||
--dnd-border-color: transparent;
|
||||
|
||||
&.is-off-screen {
|
||||
&:after {
|
||||
content: attr(data-column-name);
|
||||
font-size: var(--text-x-small);
|
||||
font-weight: 500;
|
||||
line-height: var(--column-width-collapsed);
|
||||
padding-inline: 1ch;
|
||||
position: fixed;
|
||||
text-transform: uppercase;
|
||||
top: 0;
|
||||
translate: -50%;
|
||||
}
|
||||
|
||||
&.is-collapsed {
|
||||
&:after {
|
||||
writing-mode: vertical-rl;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.is-collapsed) {
|
||||
&:after {
|
||||
background-color: var(--column-color);
|
||||
inline-size: calc(var(--column-width-expanded) - 4px); /* make room for the dnd border */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@media (any-hover: hover) {
|
||||
.card:has(.card__background img:not([src=""])):hover .card__background img:not([src=""]) {
|
||||
filter: blur(3px) brightness(1.2);
|
||||
@@ -99,6 +117,42 @@
|
||||
}
|
||||
}
|
||||
|
||||
.cards__transition-container {
|
||||
block-size: 100%;
|
||||
border-radius: calc(var(--column-width-collapsed) / 2);
|
||||
translate: 0;
|
||||
transition: translate var(--column-transition-duration) var(--ease-out-overshoot-subtle);
|
||||
|
||||
.is-collapsed:not(.cards--considering) & {
|
||||
translate: 0 var(--column-width-collapsed);
|
||||
}
|
||||
|
||||
.cards:not(.is-collapsed) & {
|
||||
padding-inline: var(--column-padding-expanded);
|
||||
}
|
||||
|
||||
.drag-and-drop__hover-container & {
|
||||
--dnd-bg-color: var(--column-color);
|
||||
--dnd-border-color: var(--card-color);
|
||||
|
||||
background-color: var(--dnd-bg-color);
|
||||
outline: 2px dashed var(--dnd-border-color);
|
||||
outline-offset: -2px;
|
||||
transition: background-color 200ms;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.no-transitions & {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
.cards__header {
|
||||
.cards.is-collapsed & {
|
||||
block-size: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* The wrapper around the cards used to clip overflow while transitioning.
|
||||
* Also, don't resize cards while transitioning to avoid reflow. */
|
||||
.cards__list {
|
||||
@@ -473,11 +527,9 @@
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
.cards--considering {
|
||||
position: relative;
|
||||
--card-color: oklch(var(--lch-blue-medium));
|
||||
|
||||
@media (min-width: 640px) {
|
||||
padding-inline: var(--cards-gap);
|
||||
}
|
||||
position: relative;
|
||||
|
||||
.card {
|
||||
--avatar-size: 2.75em;
|
||||
|
||||
@@ -7,8 +7,11 @@
|
||||
}
|
||||
|
||||
.drag-and-drop__hover-container {
|
||||
background-color: var(--color-selected-light);
|
||||
outline: 2px dashed var(--color-selected-dark);
|
||||
--dnd-bg-color: var(--color-selected-light);
|
||||
--dnd-border-color: var(--color-selected-dark);
|
||||
|
||||
background-color: var(--dnd-bg-color);
|
||||
outline: 2px dashed var(--dnd-border-color);
|
||||
outline-offset: -2px;
|
||||
transition: background-color 200ms;
|
||||
z-index: 1;
|
||||
|
||||
@@ -2,8 +2,8 @@ import { Controller } from "@hotwired/stimulus"
|
||||
import { nextFrame, debounce } from "helpers/timing_helpers";
|
||||
|
||||
export default class extends Controller {
|
||||
static classes = [ "collapsed", "noTransitions" ]
|
||||
static targets = [ "column", "button" ]
|
||||
static classes = [ "collapsed", "noTransitions", "titleNotVisible" ]
|
||||
static targets = [ "column", "button", "title" ]
|
||||
static values = {
|
||||
board: String
|
||||
}
|
||||
@@ -14,6 +14,14 @@ export default class extends Controller {
|
||||
|
||||
async connect() {
|
||||
await this.#restoreColumnsDisablingTransitions()
|
||||
this.#setupIntersectionObserver()
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
if (this._intersectionObserver) {
|
||||
this._intersectionObserver.disconnect()
|
||||
this._intersectionObserver = null
|
||||
}
|
||||
}
|
||||
|
||||
toggle({ target }) {
|
||||
@@ -106,4 +114,23 @@ export default class extends Controller {
|
||||
#localStorageKeyFor(column) {
|
||||
return `expand-${this.boardValue}-${column.getAttribute("id")}`
|
||||
}
|
||||
|
||||
#setupIntersectionObserver() {
|
||||
if (typeof IntersectionObserver === "undefined") return
|
||||
if (this._intersectionObserver) this._intersectionObserver.disconnect()
|
||||
|
||||
this._intersectionObserver = new IntersectionObserver(entries => {
|
||||
entries.forEach(entry => {
|
||||
const title = entry.target
|
||||
const column = title.closest(".cards")
|
||||
|
||||
if (!column) return
|
||||
|
||||
const offscreen = entry.intersectionRatio === 0
|
||||
column.classList.toggle(this.titleNotVisibleClass, offscreen)
|
||||
})
|
||||
}, { threshold: [0] })
|
||||
|
||||
this.titleTargets.forEach(title => this._intersectionObserver.observe(title))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,16 @@
|
||||
data-drag-and-drop-target="container"
|
||||
data-drag-and-strum-target="container"
|
||||
data-collapsible-columns-target="column"
|
||||
data-column-name="Done"
|
||||
data-action="turbo:before-morph-attribute->collapsible-columns#preventToggle"
|
||||
data-drag-and-drop-url="<%= columns_card_drops_closure_path("__id__") %>"
|
||||
>
|
||||
|
||||
<%= render "boards/show/expander", title: "Done", count: board.cards.closed.count, column_id: "closed-cards" do %>
|
||||
<%= render "boards/show/menu/expand", column_path: board_columns_closed_path(board) %>
|
||||
<% end %>
|
||||
|
||||
<%= column_frame_tag :closed_column, src: board_columns_closed_path(board) %>
|
||||
<div class="cards__transition-container">
|
||||
<header class="cards__header">
|
||||
<%= render "boards/show/expander", title: "Done", count: board.cards.closed.count, column_id: "closed-cards" do %>
|
||||
<%= render "boards/show/menu/expand", column_path: board_columns_closed_path(board) %>
|
||||
<% end %>
|
||||
</header>
|
||||
<%= column_frame_tag :closed_column, src: board_columns_closed_path(board) %>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -4,17 +4,21 @@
|
||||
data-drag-and-strum-target="container"
|
||||
data-collapsible-columns-target="column"
|
||||
data-controller="clicker"
|
||||
data-column-name="<%= column.name %>"
|
||||
data-action="turbo:before-morph-attribute->collapsible-columns#preventToggle turbo:before-stream-render@document->collapsible-columns#restoreState"
|
||||
data-drag-and-drop-url="<%= columns_card_drops_column_path("__id__", column_id: column.id) %>"
|
||||
>
|
||||
<%= render "boards/show/expander", title: column.name, count: column.cards.active.count, column_id: dom_id(column) do %>
|
||||
<%= render "boards/show/menu/column", column: column %>
|
||||
<% end %>
|
||||
<div class="cards__transition-container">
|
||||
<header class="cards__header">
|
||||
<%= render "boards/show/expander", title: column.name, count: column.cards.active.count, column_id: dom_id(column) do %>
|
||||
<%= render "boards/show/menu/column", column: column %>
|
||||
<% end %>
|
||||
|
||||
<%= link_to board_column_path(column.board, column), class: "cards__expander-button btn btn--circle txt-x-small borderless", data: { turbo_frame: "_top" } do %>
|
||||
<%= icon_tag "expand", class: "translucent" %>
|
||||
<span class="for-screen-reader">Expand column</span>
|
||||
<% end %>
|
||||
|
||||
<%= column_frame_tag dom_id(column, :cards), src: board_column_path(column.board, column) %>
|
||||
<%= link_to board_column_path(column.board, column), class: "cards__expander-button btn btn--circle txt-x-small borderless", data: { turbo_frame: "_top" } do %>
|
||||
<%= icon_tag "expand", class: "translucent" %>
|
||||
<span class="for-screen-reader">Expand column</span>
|
||||
<% end %>
|
||||
</header>
|
||||
<%= column_frame_tag dom_id(column, :cards), src: board_column_path(column.board, column) %>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -1,26 +1,28 @@
|
||||
<%= tag.div data: {
|
||||
controller: "drag-and-drop drag-and-strum",
|
||||
<%= tag.div class: "card-columns", data: {
|
||||
controller: "collapsible-columns drag-and-drop drag-and-strum",
|
||||
drag_and_drop_dragged_item_class: "drag-and-drop__dragged-item",
|
||||
drag_and_drop_hover_container_class: "drag-and-drop__hover-container",
|
||||
collapsible_columns_board_value: board.id,
|
||||
collapsible_columns_collapsed_class: "is-collapsed",
|
||||
collapsible_columns_no_transitions_class: "no-transitions",
|
||||
collapsible_columns_title_not_visible_class: "is-off-screen",
|
||||
action: "
|
||||
dragstart->drag-and-drop#dragStart
|
||||
dragover->drag-and-drop#dragOver
|
||||
dragenter->drag-and-strum#dragEnter
|
||||
drop->drag-and-drop#drop
|
||||
dragend->drag-and-drop#dragEnd" } do %>
|
||||
<div class="card-columns" data-controller="collapsible-columns" data-collapsible-columns-board-value="<%= board.id %>" data-collapsible-columns-collapsed-class="is-collapsed" data-collapsible-columns-no-transitions-class="no-transitions">
|
||||
<div class="card-columns__left">
|
||||
<%= render "boards/show/not_now", board: board %>
|
||||
</div>
|
||||
<div class="card-columns__left">
|
||||
<%= render "boards/show/not_now", board: board %>
|
||||
</div>
|
||||
|
||||
<%= render "boards/show/stream", board: board, page: page %>
|
||||
<%= render "boards/show/stream", board: board, page: page %>
|
||||
|
||||
<div class="card-columns__right">
|
||||
<%= render partial: "boards/show/column", collection: board.columns.sorted, cached: ->(column){ [ column, column.leftmost?, column.rightmost? ] } %>
|
||||
<%= render "boards/show/closed", board: board %>
|
||||
<div class="card-columns__right">
|
||||
<%= render partial: "boards/show/column", collection: board.columns.sorted, cached: ->(column){ [ column, column.leftmost?, column.rightmost? ] } %>
|
||||
<%= render "boards/show/closed", board: board %>
|
||||
|
||||
<%= render "boards/show/menu/columns", board: board %>
|
||||
</div>
|
||||
<%= render "boards/show/menu/columns", board: board %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<button class="cards__expander btn btn--plain" data-collapsible-columns-target="button" data-action="click->collapsible-columns#toggle"
|
||||
style="--card-count: <%= [ count, 20 ].min %>" aria-controls="<%= column_id %>" aria-expanded="false">
|
||||
<span class="cards__expander-count" data-drag-and-drop-counter="true"><%= count > 99 ? "99+" : count %></span>
|
||||
<h2 class="cards__expander-title"><%= title %></h2>
|
||||
<h2 class="cards__expander-title" data-collapsible-columns-target="title"><%= title %></h2>
|
||||
</button>
|
||||
<%= yield if block_given? %>
|
||||
|
||||
@@ -2,13 +2,16 @@
|
||||
data-collapsible-columns-target="column"
|
||||
data-drag-and-drop-target="container"
|
||||
data-drag-and-strum-target="container"
|
||||
data-column-name="Not Now"
|
||||
data-action="turbo:before-morph-attribute->collapsible-columns#preventToggle"
|
||||
data-drag-and-drop-url="<%= columns_card_drops_not_now_path("__id__") %>"
|
||||
>
|
||||
|
||||
<%= render "boards/show/expander", title: "Not Now", count: board.cards.postponed.count, column_id: "not-now" do %>
|
||||
<%= render "boards/show/menu/expand", column_path: board_columns_not_now_path(board) %>
|
||||
<% end %>
|
||||
|
||||
<%= column_frame_tag :not_now_column, src: board_columns_not_now_path(board) %>
|
||||
<div class="cards__transition-container">
|
||||
<header class="cards__header">
|
||||
<%= render "boards/show/expander", title: "Not Now", count: board.cards.postponed.count, column_id: "not-now" do %>
|
||||
<%= render "boards/show/menu/expand", column_path: board_columns_not_now_path(board) %>
|
||||
<% end %>
|
||||
</header>
|
||||
<%= column_frame_tag :not_now_column, src: board_columns_not_now_path(board) %>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
<section id="the-stream" class="cards cards--considering"
|
||||
data-drag-and-drop-target="container"
|
||||
data-column-name="Maybe?"
|
||||
data-action="turbo:before-morph-attribute->collapsible-columns#preventToggle"
|
||||
data-drag-and-drop-url="<%= columns_card_drops_stream_path("__id__") %>">
|
||||
<header class="cards__expander">
|
||||
<h2 class="cards__expander-title position-relative">Maybe?</h2>
|
||||
<%= link_to board_columns_stream_path(board), class: "cards__expander-button btn btn--circle txt-x-small borderless", data: { turbo_frame: "_top" } do %>
|
||||
<%= icon_tag "expand", class: "translucent" %>
|
||||
<span class="for-screen-reader">Expand column</span>
|
||||
<div class="cards__transition-container">
|
||||
<header class="cards__header">
|
||||
<div class="cards__expander">
|
||||
<h2 class="cards__expander-title position-relative" data-collapsible-columns-target="title">Maybe?</h2>
|
||||
<%= link_to board_columns_stream_path(board), class: "cards__expander-button btn btn--circle txt-x-small borderless", data: { turbo_frame: "_top" } do %>
|
||||
<%= icon_tag "expand", class: "translucent" %>
|
||||
<span class="for-screen-reader">Expand column</span>
|
||||
<% end %>
|
||||
</div>
|
||||
</header>
|
||||
<%= render "columns/show/add_card_button", board: board %>
|
||||
<% if page.used? %>
|
||||
<%= with_automatic_pagination "the-stream", @page do %>
|
||||
<%= render "boards/columns/list", cards: page.records, draggable: true %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</header>
|
||||
|
||||
<%= render "columns/show/add_card_button", board: board %>
|
||||
|
||||
<% if page.used? %>
|
||||
<%= with_automatic_pagination "the-stream", @page do %>
|
||||
<%= render "boards/columns/list", cards: page.records, draggable: true %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<section id="the-stream" class="cards cards--considering"
|
||||
<section id="the-stream" class="cards cards--considering" data-column-name="Maybe?"
|
||||
data-action="turbo:before-morph-attribute->collapsible-columns#preventToggle">
|
||||
<header class="cards__expander">
|
||||
<h2 class="cards__expander-title position-relative"">Maybe?</h2>
|
||||
<h2 class="cards__expander-title position-relative" data-collapsible-columns-target="title">Maybe?</h2>
|
||||
</header>
|
||||
|
||||
<% if page.used? %>
|
||||
|
||||
Reference in New Issue
Block a user