Files
Ely-GPUI-Components/src/project/moves.rs
T
ZacharyZhang-NY 8cbd389d72 Project · Issues and boards: a board, issue cards, a task's panel, labels
KanbanBoard lays columns of cards side by side. A card drags to a
place in any column: it rides raised at the pointer, a slot opens where
it would land, the other cards glide aside on motion::Flip, and the
drop asks the owner through on_move(card, column, index); a drop in its
own place asks nothing. Option with an arrow moves the focused card,
whose focus the board keys by card so it follows it across columns. A
press, Enter or Space opens it. Sprint Board is this board.

IssueCard shows an issue's status, name, assignee, title, priority,
labels and due day. TaskDetailPanel lays a task out in full: its title
edits in place, its status, priority, assignee and due day change where
they stand, and its notes and steps follow; each edit hands the whole
task on, and a blank title is not taken. LabelManager tends a team's
labels: a hue picked from a popover, a name edited in place, a delete,
and a field that makes a new one; names stay unique in any case, and a
refused name says why.

The fill rule changes: min_w_full, taken up in T30d, resolves against
a block's outer width in taffy 0.9, so a field in a padded card ran
past its padding. A component that fills now takes no width on its
root, which blocks and flex columns stretch; FormField and the new
roots do. The chart's hue names move to theme::HUE_NAMES, which the
event editor and the label manager share.
2026-09-27 05:12:20 -04:00

59 lines
2.0 KiB
Rust

use gpui::Pixels;
/// Where a card whose middle sits at `middle` lands among rows of `heights` from a column's `top`: past every row whose middle is above it.
pub(crate) fn landing(heights: &[Pixels], top: Pixels, middle: Pixels) -> usize {
let mut y = top;
let mut index = 0;
for height in heights {
if y + *height / 2.0 < middle {
index += 1;
}
y += *height;
}
index
}
/// Where Option and an arrow take the card at `index` of `column`, among columns of `lens` cards: up and down within it, left and right to the same place or the end.
pub(crate) fn stepped(
lens: &[usize],
column: usize,
index: usize,
key: &str,
) -> Option<(usize, usize)> {
match key {
"up" => (index > 0).then(|| (column, index - 1)),
"down" => (index + 1 < lens[column]).then(|| (column, index + 1)),
"left" => (column > 0).then(|| (column - 1, index.min(lens[column - 1]))),
"right" => (column + 1 < lens.len()).then(|| (column + 1, index.min(lens[column + 1]))),
_ => None,
}
}
#[cfg(test)]
mod tests {
use gpui::px;
use super::{landing, stepped};
#[test]
fn a_card_lands_past_the_rows_whose_middles_it_passed() {
let rows = [px(40.0), px(60.0), px(40.0)];
assert_eq!(landing(&rows, px(100.0), px(110.0)), 0);
assert_eq!(landing(&rows, px(100.0), px(125.0)), 1);
assert_eq!(landing(&rows, px(100.0), px(175.0)), 2);
assert_eq!(landing(&rows, px(100.0), px(400.0)), 3);
}
#[test]
fn option_arrows_step_within_and_across_columns() {
let lens = [3, 0, 2];
assert_eq!(stepped(&lens, 0, 0, "up"), None);
assert_eq!(stepped(&lens, 0, 1, "down"), Some((0, 2)));
assert_eq!(stepped(&lens, 0, 2, "down"), None);
assert_eq!(stepped(&lens, 0, 2, "right"), Some((1, 0)));
assert_eq!(stepped(&lens, 2, 1, "left"), Some((1, 0)));
assert_eq!(stepped(&lens, 2, 1, "right"), None);
assert_eq!(stepped(&lens, 1, 0, "home"), None);
}
}