Merge pull request #1430 from basecamp/jz/10-30-25

JZ 10/30/25
This commit is contained in:
Jason Zimdars
2025-10-30 14:10:20 -05:00
committed by GitHub
14 changed files with 68 additions and 53 deletions
+25
View File
@@ -39,6 +39,31 @@
opacity: 0.3;
pointer-events: none;
}
[aria-busy] &:disabled {
position: relative;
> * {
visibility: hidden;
}
&::after {
--mask: no-repeat radial-gradient(#000 68%,#0000 71%);
--size: 1.25em;
-webkit-mask: var(--mask), var(--mask), var(--mask);
-webkit-mask-size: 28% 45%;
animation: submitting 1s infinite linear;
aspect-ratio: 8/5;
background: currentColor;
content: "";
inline-size: var(--size);
inset: 50%;
margin-block: calc((var(--size) / 3) * -1);
margin-inline: calc((var(--size) / 2) * -1);
position: absolute;
}
}
}
/* Variants
+4
View File
@@ -208,6 +208,10 @@
z-index: -1;
}
.no-transitions &:after {
transition: none;
}
.cards.is-collapsed & {
block-size: 100%;
flex-direction: column;
+2
View File
@@ -126,6 +126,7 @@
}
.comment__body {
padding: 0;
text-align: center;
}
@@ -135,6 +136,7 @@
var(--color-canvas) 0 1px,
var(--color-ink-lightest) 1px 10px
);
padding-inline: var(--comment-padding-inline);
}
.reactions {
+4 -1
View File
@@ -8,7 +8,6 @@
--popup-item-padding-inline: 0.5rem;
inset: 0 auto auto 50%;
max-block-size: 80vh;
min-inline-size: min(25ch, calc(100dvw - var(--block-space-double)));
max-inline-size: min(55ch, calc(100dvw - var(--block-space-double)));
overflow: auto;
@@ -32,6 +31,10 @@
&.orient-top {
inset-block: auto 0;
&.orient-bottom {
inset-block: 0 auto;
}
}
&:has(.popup__footer) {
+2 -2
View File
@@ -19,12 +19,12 @@
text-overflow: ellipsis;
&.orient-right {
inset-inline-start: 0;
inset-inline: 0 auto;
translate: 0 -100%;
}
&.orient-left {
inset-inline-end: 0;
inset-inline: auto 0;
translate: 0 -100%;
}
}
+1 -1
View File
@@ -4,7 +4,7 @@ module ColumnsHelper
tag.span(column.name, class: "overflow-ellipsis"),
card_triage_path(card, column_id: column),
method: :post,
class: [ "card__column-name btn", { "card__column-name--current": column == card.column } ],
class: [ "card__column-name btn", { "card__column-name--current": column == card.column && card.open? } ],
form_class: "flex align-center gap-half",
data: { turbo_frame: "_top" }
end
@@ -1,43 +0,0 @@
import { Controller } from "@hotwired/stimulus"
const BOTTOM_THRESHOLD = 0
export default class extends Controller {
static targets = [ "menu" ]
static classes = [ "orientationTop" ]
close() {
this.menuTarget.close()
this.#orient()
}
open() {
this.menuTarget.show()
this.#orient()
}
toggle() {
this.menuTarget.open ? this.close() : this.open()
}
closeOnClickOutside({ target }) {
if (!this.element.contains(target)) this.close()
}
#orient() {
this.element.classList.toggle(this.orientationTopClass, this.#distanceToBottom < BOTTOM_THRESHOLD)
this.menuTarget.style.setProperty("--max-width", this.#maxWidth + "px")
}
get #distanceToBottom() {
return window.innerHeight - this.#boundingClientRect.bottom
}
get #maxWidth() {
return window.innerWidth - this.#boundingClientRect.left
}
get #boundingClientRect() {
return this.menuTarget.getBoundingClientRect()
}
}
@@ -1,5 +1,6 @@
import { Controller } from "@hotwired/stimulus"
import { orient } from "helpers/orientation_helpers"
import { limitHeightToViewport } from "helpers/sizing_helpers"
export default class extends Controller {
static targets = [ "dialog" ]
@@ -21,6 +22,8 @@ export default class extends Controller {
orient(this.dialogTarget)
}
limitHeightToViewport(this.dialogTarget, true)
this.dialogTarget.setAttribute("aria-hidden", "false")
this.dispatch("show")
}
@@ -38,6 +41,7 @@ export default class extends Controller {
this.dialogTarget.setAttribute("aria-hidden", "true")
this.dialogTarget.blur()
orient(this.dialogTarget, false)
limitHeightToViewport(this.dialogTarget, false)
}
closeOnClickOutside({ target }) {
@@ -24,6 +24,7 @@ export default class extends Controller {
checkNone() {
this.checkboxTargets.forEach(checkbox => {
if (checkbox.dataset.collectionsFormTarget === "meCheckbox") return
checkbox.checked = false
})
}
@@ -4,7 +4,8 @@ export function orient(el, orient = true) {
const directions = [
["orient-left", spaceOnRight],
["orient-right", spaceOnLeft],
["orient-top", spaceOnBottom]
["orient-top", spaceOnBottom],
["orient-bottom", spaceOnTop]
];
directions.forEach(([className, fn]) =>
@@ -23,3 +24,7 @@ function spaceOnRight(el) {
function spaceOnBottom(el) {
return window.innerHeight - el.getBoundingClientRect().bottom
}
function spaceOnTop(el) {
return el.getBoundingClientRect().top
}
+12
View File
@@ -0,0 +1,12 @@
export function limitHeightToViewport(el, sizing = true, margin = 64) {
if (!el) return
if (sizing) {
const rect = el.getBoundingClientRect()
const top = Math.max(rect.top, margin)
const max = Math.max(0, window.innerHeight - margin - top)
el.style.maxHeight = `${max}px`
} else {
el.style.maxHeight = ""
}
}
+4 -1
View File
@@ -19,7 +19,10 @@
<%= render "cards/container/title", card: card %>
<%= render "cards/display/perma/steps", card: card %>
</div>
<%= render "cards/triage/columns", card: card %>
<% if card.published? %>
<%= render "cards/triage/columns", card: card %>
<% end %>
</div>
<footer class="card__footer full-width flex align-start gap">
+1 -1
View File
@@ -8,7 +8,7 @@
<%= form_with model: Comment.new, url: card_comments_path(card), class: "flex flex-column gap full-width",
data: { controller: "form local-save",
local_save_key_value: "comment-#{card.id}",
action: "turbo:submit-end->local-save#submit keydown.ctrl+enter->form#submit:prevent keydown.meta+enter->form#submit:prevent keydown.esc->form#cancel:stop" } do |form| %>
action: "turbo:submit-end->local-save#submit keydown.ctrl+enter->form#debouncedSubmit:prevent keydown.meta+enter->form#debouncedSubmit:prevent keydown.esc->form#cancel:stop" } do |form| %>
<%= form.rich_textarea :body, required: true, placeholder: new_comment_placeholder(card),
data: { local_save_target: "input", action: "lexxy:change->form#disableSubmitWhenInvalid lexxy:change->local-save#save turbo:morph-element->local-save#restoreContent" } do %>
<%= general_prompts(@card.collection) %>
+2 -3
View File
@@ -23,9 +23,8 @@
<%= render "filters/menu/hotkeys" %>
<div class="margin-block-start-half txt-x-small flex-inline align-center gap-half justify-center txt-subtle">
<span>BOXCAR is designed, built, and backed by</span>
<strong><%= icon_tag "37signals" %> <a href="https://37signals.com" target="_blank" class="txt-subtle">37signals</a>.</strong>
<div class="margin-block-start-half txt-x-small txt-align-center txt-subtle">
BOXCAR is designed, built, and backed by <strong class="txt-nowrap"><%= icon_tag "37signals" %> <a href="https://37signals.com" target="_blank" class="txt-subtle">37signals</a>.</strong>
</div>
<%= render "filters/menu/custom", filters: @user_filtering.filters %>