Signed by hannaSSH key fingerprint: SHA256:4g9cWhkAAw8gwqhJUcVbSnGEvdGwklW+1Aa/fMUu59k
assets/base.css +51 −14
| @@ -516,9 +516,13 @@ body.drawer-open .drawer-backdrop { | |||
| 516 | 516 | } | |
| 517 | 517 | .issue-title-line { | |
| 518 | 518 | display: flex; | |
| 519 | - | align-items: flex-start; | |
| 519 | + | align-items: center; | |
| 520 | 520 | justify-content: space-between; | |
| 521 | - | gap: 1rem; | |
| 521 | + | gap: 0.5rem; | |
| 522 | + | } | |
| 523 | + | /* Hide the title while its editor is open (GitHub-style inline rename). */ | |
| 524 | + | .issue-title-line:has(.issue-edit[open]) h1 { | |
| 525 | + | display: none; | |
| 522 | 526 | } | |
| 523 | 527 | .issue-edit, | |
| 524 | 528 | .comment-edit { | |
| @@ -533,30 +537,63 @@ body.drawer-open .drawer-backdrop { | |||
| 533 | 537 | .comment-edit > summary::-webkit-details-marker { | |
| 534 | 538 | display: none; | |
| 535 | 539 | } | |
| 536 | - | .issue-edit .card { | |
| 537 | - | margin-top: 0.5rem; | |
| 538 | - | width: min(32rem, 90vw); | |
| 540 | + | /* Edit / Cancel label swap on the toggle. */ | |
| 541 | + | .issue-edit:not([open]) .edit-close, | |
| 542 | + | .comment-edit:not([open]) .edit-close { | |
| 543 | + | display: none; | |
| 544 | + | } | |
| 545 | + | .issue-edit[open] .edit-open, | |
| 546 | + | .comment-edit[open] .edit-open { | |
| 547 | + | display: none; | |
| 548 | + | } | |
| 549 | + | /* Title editor: a full-width input with Cancel (the toggle) and Save beside it. */ | |
| 550 | + | .issue-edit[open] { | |
| 551 | + | display: flex; | |
| 552 | + | align-items: center; | |
| 553 | + | gap: 0.5rem; | |
| 554 | + | flex: 1 1 100%; | |
| 555 | + | } | |
| 556 | + | .issue-edit[open] > summary { | |
| 557 | + | order: 2; | |
| 558 | + | } | |
| 559 | + | .title-edit-form { | |
| 560 | + | order: 1; | |
| 561 | + | flex: 1; | |
| 562 | + | } | |
| 563 | + | .title-edit-form input[type="text"] { | |
| 564 | + | width: 100%; | |
| 539 | 565 | } | |
| 566 | + | .title-save { | |
| 567 | + | order: 3; | |
| 568 | + | } | |
| 569 | + | /* Comment editor: the toggle sits in the header; when open the rendered body is | |
| 570 | + | replaced by the textarea. */ | |
| 540 | 571 | .comment-edit { | |
| 541 | 572 | margin-left: auto; | |
| 542 | 573 | } | |
| 543 | - | /* When opened, break onto its own full-width row so the form isn't cramped. */ | |
| 544 | - | .comment-edit[open] { | |
| 545 | - | flex-basis: 100%; | |
| 546 | - | margin-left: 0; | |
| 547 | - | margin-top: 0.4rem; | |
| 548 | - | } | |
| 549 | 574 | .comment-edit > summary { | |
| 550 | 575 | font-size: 0.9em; | |
| 551 | 576 | color: var(--fb-accent); | |
| 552 | 577 | } | |
| 553 | - | .comment-edit form { | |
| 554 | - | margin-top: 0.5rem; | |
| 578 | + | .comment-editor { | |
| 579 | + | display: none; | |
| 580 | + | padding: 0.85rem; | |
| 555 | 581 | } | |
| 556 | - | .comment-edit textarea { | |
| 582 | + | .comment:has(.comment-edit[open]) .comment-editor { | |
| 583 | + | display: block; | |
| 584 | + | } | |
| 585 | + | .comment:has(.comment-edit[open]) .comment-body { | |
| 586 | + | display: none; | |
| 587 | + | } | |
| 588 | + | .comment-editor textarea { | |
| 557 | 589 | width: 100%; | |
| 558 | 590 | font-family: inherit; | |
| 559 | 591 | } | |
| 592 | + | .comment-editor-actions { | |
| 593 | + | display: flex; | |
| 594 | + | justify-content: flex-end; | |
| 595 | + | margin-top: 0.5rem; | |
| 596 | + | } | |
| 560 | 597 | .issue-sub { | |
| 561 | 598 | display: flex; | |
| 562 | 599 | align-items: center; | |
crates/web/src/issues.rs +30 −18
| @@ -247,7 +247,7 @@ pub(crate) async fn view( | |||
| 247 | 247 | div class="issue-title-line" { | |
| 248 | 248 | h1 { (issue.title) " " span class="muted" { "#" (issue.number) } } | |
| 249 | 249 | @if can_write || is_author { | |
| 250 | - | (edit_issue_form(&issue, kind, &csrf)) | |
| 250 | + | (edit_issue_form(&issue, &csrf)) | |
| 251 | 251 | } | |
| 252 | 252 | } | |
| 253 | 253 | div class="issue-sub" { | |
| @@ -316,18 +316,27 @@ pub(crate) fn comment_card( | |||
| 316 | 316 | div class="comment-head muted" { | |
| 317 | 317 | strong { (author) } | |
| 318 | 318 | span { " commented " (crate::activity::fmt_relative(created_at, crate::now_ms())) } | |
| 319 | - | @if let Some((action, csrf)) = edit { | |
| 319 | + | // A bare toggle; CSS `:has(.comment-edit[open])` swaps the rendered | |
| 320 | + | // body for the editor below, so no JavaScript is needed. | |
| 321 | + | @if edit.is_some() { | |
| 320 | 322 | details class="comment-edit" { | |
| 321 | - | summary { "Edit" } | |
| 322 | - | form method="post" action=(action) { | |
| 323 | - | input type="hidden" name="_csrf" value=(csrf); | |
| 324 | - | textarea name="body" rows="4" required { (body) } | |
| 325 | - | button class="btn inline-btn" type="submit" { "Save" } | |
| 323 | + | summary { | |
| 324 | + | span class="edit-open" { "Edit" } | |
| 325 | + | span class="edit-close" { "Cancel" } | |
| 326 | 326 | } | |
| 327 | 327 | } | |
| 328 | 328 | } | |
| 329 | 329 | } | |
| 330 | 330 | div class="comment-body markdown" { (markdown::render(body)) } | |
| 331 | + | @if let Some((action, csrf)) = edit { | |
| 332 | + | form class="comment-editor" method="post" action=(action) { | |
| 333 | + | input type="hidden" name="_csrf" value=(csrf); | |
| 334 | + | textarea name="body" rows="6" required { (body) } | |
| 335 | + | div class="comment-editor-actions" { | |
| 336 | + | button class="btn btn-primary inline-btn" type="submit" { "Save" } | |
| 337 | + | } | |
| 338 | + | } | |
| 339 | + | } | |
| 331 | 340 | } | |
| 332 | 341 | } | |
| 333 | 342 | } | |
| @@ -416,20 +425,23 @@ pub(crate) fn lock_button(issue: &Issue, csrf: &str) -> Markup { | |||
| 416 | 425 | } | |
| 417 | 426 | } | |
| 418 | 427 | ||
| 419 | - | /// The edit-title form, revealed by a toggle in the issue/PR header. (The | |
| 420 | - | /// description is edited from its own comment card.) | |
| 421 | - | pub(crate) fn edit_issue_form(issue: &Issue, kind: Kind, csrf: &str) -> Markup { | |
| 428 | + | /// The edit-title toggle in the issue/PR header. When open it hides the title and | |
| 429 | + | /// shows a full-width single-line input with Cancel (the toggle) and Save beside | |
| 430 | + | /// it, GitHub-style. (The description is edited from its own comment card.) | |
| 431 | + | pub(crate) fn edit_issue_form(issue: &Issue, csrf: &str) -> Markup { | |
| 432 | + | let form_id = format!("title-edit-{}", issue.id); | |
| 422 | 433 | html! { | |
| 423 | 434 | details class="issue-edit" { | |
| 424 | - | summary class="btn" { "Edit" } | |
| 425 | - | div class="card" { | |
| 426 | - | form method="post" action=(format!("/issue/{}/edit-title", issue.id)) { | |
| 427 | - | input type="hidden" name="_csrf" value=(csrf); | |
| 428 | - | label for="edit-title" { "Title" } | |
| 429 | - | input type="text" id="edit-title" name="title" value=(issue.title) required; | |
| 430 | - | button class="btn btn-primary inline-btn" type="submit" { "Save " (kind.noun) " title" } | |
| 431 | - | } | |
| 435 | + | summary class="btn edit-toggle" { | |
| 436 | + | span class="edit-open" { "Edit" } | |
| 437 | + | span class="edit-close" { "Cancel" } | |
| 438 | + | } | |
| 439 | + | form id=(form_id) class="title-edit-form" method="post" | |
| 440 | + | action=(format!("/issue/{}/edit-title", issue.id)) { | |
| 441 | + | input type="hidden" name="_csrf" value=(csrf); | |
| 442 | + | input type="text" name="title" value=(issue.title) aria-label="Title" required; | |
| 432 | 443 | } | |
| 444 | + | button class="btn btn-primary title-save" type="submit" form=(form_id) { "Save" } | |
| 433 | 445 | } | |
| 434 | 446 | } | |
| 435 | 447 | } | |
crates/web/src/pulls.rs +1 −1
| @@ -142,7 +142,7 @@ pub(crate) async fn view( | |||
| 142 | 142 | div class="issue-title-line" { | |
| 143 | 143 | h1 { (issue.title) " " span class="muted" { "#" (issue.number) } } | |
| 144 | 144 | @if can_write || is_author { | |
| 145 | - | (edit_issue_form(&issue, Kind::pulls(), &csrf)) | |
| 145 | + | (edit_issue_form(&issue, &csrf)) | |
| 146 | 146 | } | |
| 147 | 147 | } | |
| 148 | 148 | div class="issue-sub" { | |