Signed by hanna SSH key fingerprint: SHA256:4g9cWhkAAw8gwqhJUcVbSnGEvdGwklW+1Aa/fMUu59k
assets/base.css +12 −0 assets/fabrica.js +18 −0 crates/web/src/issues.rs +15 −9 crates/web/src/layout.rs +10 −18 crates/web/src/lib.rs +2 −0 crates/web/src/pages.rs +63 −0 assets/base.css +12 −0 Expand 176 hidden lines 177 177 white-space : nowrap ; 178 178 } 179 179 180 + . label-dropdown-wrap { 181 + display : flex ; 182 + align-items : center ; 183 + flex-wrap : wrap ; 184 + gap : 0.4rem ; 185 + margin-bottom : 0.5rem ; 186 + } 187 + . label-dropdown-chips { 188 + display : flex ; 189 + flex-wrap : wrap ; 190 + gap : 0.35rem ; 191 + } 180 192 . label-dropdown { 181 193 display : inline-block ; 182 194 position : relative ;
assets/fabrica.js +18 −0 Expand 64 hidden lines 65 65 } ) ; 66 66 } ) ; 67 67 68 + 69 + document . querySelectorAll ( "[data-label-dropdown]" ) . forEach ( function ( dd ) { 70 + var chips = dd . parentNode. querySelector ( "[data-label-chips]" ) ; 71 + if ( ! chips ) return ; 72 + function sync ( ) { 73 + chips . textContent = "" ; 74 + dd . querySelectorAll ( 'input[type="checkbox"]:checked' ) . forEach ( function ( cb ) { 75 + var chip = document . createElement ( "span" ) ; 76 + chip . className = "label-chip" ; 77 + chip . style. setProperty ( "--label-color" , cb . getAttribute ( "data-label-color" ) || "#888" ) ; 78 + chip . textContent = cb . getAttribute ( "data-label-name" ) || cb . value; 79 + chips . appendChild ( chip ) ; 80 + } ) ; 81 + } 82 + dd . addEventListener ( "change" , sync ) ; 83 + sync ( ) ; 84 + } ) ; 85 + 68 86 69 87 70 88
crates/web/src/issues.rs +15 −9 Expand 503 hidden lines 504 504 } 505 505 506 506 507 - 507 + 508 + 508 509 fn label_dropdown ( repo_labels : & [ Label ] ) -> Markup { 509 510 html ! { 510 - details class="menu label-dropdown" data-menu { 511 - summary class="btn" { "Select labels" ( icon ( Icon :: ChevronDown ) ) } 512 - div class="menu-popover label-menu" { 513 - @for l in repo_labels { 514 - label class="checkbox" { 515 - input type ="checkbox" name="label" value=( l. id) ; 516 - " " span class="label-swatch" style=( format ! ( "--label-color: {}" , css_color ( & l. color) ) ) { } 517 - " " ( l. name) 511 + div class="label-dropdown-wrap" { 512 + details class="menu label-dropdown" data-menu data-label-dropdown { 513 + summary class="btn" { "Select labels " ( icon ( Icon :: ChevronDown ) ) } 514 + div class="menu-popover label-menu" { 515 + @for l in repo_labels { 516 + label class="checkbox" { 517 + input type ="checkbox" name="label" value=( l. id) 518 + data-label-name=( l. name) 519 + data-label-color=( css_color ( & l. color) ) ; 520 + " " span class="label-swatch" style=( format ! ( "--label-color: {}" , css_color ( & l. color) ) ) { } 521 + " " ( l. name) 522 + } 518 523 } 519 524 } 520 525 } 526 + span class="label-dropdown-chips" data-label-chips { } 521 527 } 522 528 } 523 529 }
crates/web/src/layout.rs +10 −18 Expand 83 hidden lines 84 84 } 85 85 } 86 86 87 - 88 - 89 - 90 - fn create_menu ( chrome : & Chrome ) -> Markup { 91 - let repo_base = repo_base ( & chrome. path) ; 87 + 88 + 89 + 90 + 91 + fn create_menu ( _chrome : & Chrome ) -> Markup { 92 92 html ! { 93 93 details class="topbar-create menu" data-menu { 94 94 summary class="icon-btn" aria-label="Create new" title="Create new" { Expand 3 hidden lines 98 98 a class="menu-item" role="menuitem" href="/new" { 99 99 ( icon ( Icon :: Box ) ) span { "New repository" } 100 100 } 101 - @if let Some ( base) = & repo_base { 102 - a class="menu-item" role="menuitem" href=( format ! ( "{base}/-/issues/new" ) ) { 103 - ( icon ( Icon :: Issue ) ) span { "New issue" } 104 - } 105 - a class="menu-item" role="menuitem" href=( format ! ( "{base}/-/pulls/new" ) ) { 106 - ( icon ( Icon :: GitPull ) ) span { "New pull request" } 107 - } 101 + a class="menu-item" role="menuitem" href="/new/issue" { 102 + ( icon ( Icon :: Issue ) ) span { "New issue" } 103 + } 104 + a class="menu-item" role="menuitem" href="/new/pull" { 105 + ( icon ( Icon :: GitPull ) ) span { "New pull request" } 108 106 } 109 107 } 110 108 } 111 109 } 112 110 } 113 111 114 - 115 - 116 - fn repo_base ( path : & str ) -> Option < String > { 117 - path. split_once ( "/-/" ) . map ( |( base, _) | base. to_string ( ) ) 118 - } 119 - 120 112 121 113 fn drawer ( chrome : & Chrome ) -> Markup { 122 114 html ! {
crates/web/src/lib.rs +2 −0 Expand 137 hidden lines 138 138 "/new" , 139 139 get ( pages:: new_repo_form) . post ( pages:: new_repo_submit) , 140 140 ) 141 + . route ( "/new/issue" , get ( pages:: new_issue_chooser) ) 142 + . route ( "/new/pull" , get ( pages:: new_pull_chooser) ) 141 143 . route ( "/explore" , get ( pages:: explore) ) 142 144 . route ( "/explore/users" , get ( pages:: explore_users) ) 143 145 . route ( "/login" , get ( pages:: login_form) . post ( pages:: login_submit) )
crates/web/src/pages.rs +63 −0 Expand 247 hidden lines 248 248 } ) 249 249 } 250 250 251 + 252 + pub async fn new_issue_chooser ( 253 + State ( state) : State < AppState > , 254 + RequireUser ( user) : RequireUser , 255 + jar : CookieJar , 256 + uri : Uri , 257 + ) -> AppResult < Response > { 258 + let body = repo_chooser ( & state, & user, "issues" , "issue" ) . await ?; 259 + let ( jar, chrome) = build_chrome ( & state, jar, Some ( user) , uri. path ( ) . to_string ( ) ) ; 260 + Ok ( ( jar, page ( & chrome, "New issue" , body) ) . into_response ( ) ) 261 + } 262 + 263 + 264 + pub async fn new_pull_chooser ( 265 + State ( state) : State < AppState > , 266 + RequireUser ( user) : RequireUser , 267 + jar : CookieJar , 268 + uri : Uri , 269 + ) -> AppResult < Response > { 270 + let body = repo_chooser ( & state, & user, "pulls" , "pull request" ) . await ?; 271 + let ( jar, chrome) = build_chrome ( & state, jar, Some ( user) , uri. path ( ) . to_string ( ) ) ; 272 + Ok ( ( jar, page ( & chrome, "New pull request" , body) ) . into_response ( ) ) 273 + } 274 + 275 + 276 + 277 + async fn repo_chooser ( state : & AppState , user : & User , seg : & str , noun : & str ) -> AppResult < Markup > { 278 + let repos = state. store. repos_by_owner ( & user. id) . await ?; 279 + let usable: Vec < _ > = repos 280 + . iter ( ) 281 + . filter ( |r| { 282 + if seg == "pulls" { 283 + r. pulls_enabled 284 + } else { 285 + r. issues_enabled 286 + } 287 + } ) 288 + . collect ( ) ; 289 + Ok ( html ! { 290 + div class="new-repo" { 291 + h1 { "New " ( noun) } 292 + div class="card" { 293 + p class="muted field-hint" { "Choose a repository." } 294 + @if usable. is_empty ( ) { 295 + p class="muted" { "You have no repositories with " ( noun) "s enabled." } 296 + } @else { 297 + ul class="repo-list" { 298 + @for r in & usable { 299 + li { 300 + a class="repo-row" href={ "/" ( user. username) "/" ( r. path) "/-/" ( seg) "/new" } { 301 + span class="repo-row-name" { 302 + ( icon ( Icon :: Box ) ) span { ( r. path) } 303 + } 304 + } 305 + } 306 + } 307 + } 308 + } 309 + } 310 + } 311 + } ) 312 + } 313 + 251 314 252 315 pub async fn new_repo_form ( 253 316 State ( state) : State < AppState > ,