11import { customElement , property , state } from "lit/decorators.js" ;
22import { css , html , nothing } from "lit" ;
33import { DocksContainer } from "./container" ;
4+ import { appLoaderService } from "../core/apploader" ;
45import { contributionRegistry , ContributionChangeEvent , TabContribution , TOPIC_CONTRIBUTEIONS_CHANGED } from "../core/contributionregistry" ;
56import { when } from "lit/directives/when.js" ;
67import { repeat } from "lit/directives/repeat.js" ;
@@ -12,8 +13,6 @@ import {MouseButton, EDITOR_AREA_MAIN} from "../core/constants";
1213import { activePartSignal , activeEditorSignal , partDirtySignal } from "../core/appstate" ;
1314import { watchSignal } from "../core/signals" ;
1415import { confirmDialog } from "../dialogs" ;
15- import { appLoaderService } from "../core/apploader" ;
16-
1716/**
1817 * DocksTabs - A dynamic tab container component
1918 *
@@ -32,6 +31,16 @@ export class DocksTabs extends DocksContainer {
3231 @property ( { reflect : true } )
3332 placement : "top" | "bottom" | "start" | "end" = "top" ;
3433
34+ /** When true, tab contributions show icons only (labels remain on title + wa-icon for a11y). */
35+ @property ( { type : Boolean , reflect : true , attribute : 'icon-only' } )
36+ iconOnly : boolean = false ;
37+
38+ /**
39+ * When true, render a `docks-toolbar` in the tab group `nav` slot after tabs (extensions contribute via id `${containerId}-toolbar`).
40+ */
41+ @property ( { type : Boolean , reflect : true , attribute : 'with-toolbar' } )
42+ withToolbar : boolean = false ;
43+
3544 /** Tab contributions for this container */
3645 @state ( )
3746 private contributions : TabContribution [ ] = [ ] ;
@@ -60,11 +69,14 @@ export class DocksTabs extends DocksContainer {
6069 this . updateComplete . then ( ( ) => this . ensureTabGroupListenersAndActivate ( ) ) ;
6170
6271 subscribe ( TOPIC_CONTRIBUTEIONS_CHANGED , ( event : ContributionChangeEvent ) => {
63- if ( ! this . containerId || event . target !== this . containerId ) return ;
64-
72+ if ( ! this . containerId ) return ;
73+ const navToolbarId = `${ this . containerId } -toolbar` ;
74+ if ( event . target === navToolbarId ) return ;
75+ if ( event . target !== this . containerId ) return ;
76+
6577 this . loadAndResolveContributions ( ) ;
6678 this . requestUpdate ( ) ;
67-
79+
6880 this . updateComplete . then ( ( ) => {
6981 this . activateNextAvailableTab ( ) ;
7082 } ) ;
@@ -326,6 +338,11 @@ export class DocksTabs extends DocksContainer {
326338 return label . slice ( 0 , startLen ) + ellipsis + label . slice ( - ( take - startLen ) ) ;
327339 }
328340
341+ /** Aligns `docks-toolbar` with `placement`: side rails → vertical, top/bottom tab strip → horizontal. */
342+ private withToolbarOrientation ( ) : "horizontal" | "vertical" {
343+ return this . placement === "start" || this . placement === "end" ? "vertical" : "horizontal" ;
344+ }
345+
329346 private renderEmptyState ( ) {
330347 const currentApp = appLoaderService . getCurrentApp ( ) ;
331348 return html `
@@ -351,6 +368,7 @@ export class DocksTabs extends DocksContainer {
351368 if ( this . contributions . length === 0 ) {
352369 return this . renderEmptyState ( ) ;
353370 }
371+ const navToolbarId = this . containerId ? `${ this . containerId } -toolbar` : '' ;
354372 return html `
355373 <wa- tab- group ${ ref ( this . tabGroup ) } placement= ${ this . placement } >
356374 ${ repeat (
@@ -364,7 +382,7 @@ export class DocksTabs extends DocksContainer {
364382 title = "${ fullLabel } "
365383 @auxclick = "${ ( e : MouseEvent ) => this . handleTabAuxClick ( e , c ) } ">
366384 ${ icon ( c . icon , { label : fullLabel } ) }
367- ${ shortLabel }
385+ ${ this . iconOnly ? nothing : shortLabel }
368386 ${ when ( c . closable , ( ) => html `
369387 <wa- icon name= "xmark" label = "Close" @click = "${ ( e : Event ) => this . closeTab ( e , c . name ) } "> </ wa- icon>
370388 ` ) }
@@ -375,6 +393,18 @@ export class DocksTabs extends DocksContainer {
375393 ` ;
376394 }
377395 ) }
396+ ${ this . withToolbar && navToolbarId
397+ ? html `
398+ <div class= "nav-toolbar-spacer" slot = "nav" aria- hidden= "true"> </ div>
399+ <docks- to olbar
400+ slot= "nav"
401+ id = ${ navToolbarId }
402+ or ientation= ${ this . withToolbarOrientation ( ) }
403+ align= "center"
404+ size= "large"
405+ > </ docks- to olbar>
406+ `
407+ : nothing }
378408 </ wa- tab- group>
379409 ` ;
380410 }
@@ -388,15 +418,22 @@ export class DocksTabs extends DocksContainer {
388418 wa-tab-group {
389419 height : 100% ;
390420 width : 100% ;
421+ min-height : 0 ;
391422 }
392423
393- wa-tab-group ::part (base ) {
424+ : host ( : is ([ placement = "top" ] , [ placement = "bottom" ])) wa-tab-group ::part (base ) {
394425 display : grid;
395426 grid-template-rows : auto minmax (0 , 1fr );
396427 height : 100% ;
397428 width : 100% ;
398429 }
399430
431+ : host (: is ([placement = "start" ], [placement = "end" ])) wa-tab-group ::part (base ) {
432+ height : 100% ;
433+ width : 100% ;
434+ min-height : 0 ;
435+ }
436+
400437 wa-tab-panel [active ] {
401438 display : grid;
402439 grid-template-rows : minmax (0 , 1fr );
@@ -416,6 +453,56 @@ export class DocksTabs extends DocksContainer {
416453 padding : 3px 0.5rem ;
417454 }
418455
456+ : host ([icon-only ]) wa-tab ::part (base ) {
457+ justify-content : center;
458+ }
459+
460+ : host ([icon-only ]: is ([placement = "top" ], [placement = "bottom" ])) wa-tab ::part (base ) {
461+ padding : var (--wa-space-s );
462+ }
463+
464+ : host ([icon-only ]: is ([placement = "start" ], [placement = "end" ])) wa-tab ::part (base ) {
465+ padding-inline : 0 ;
466+ padding-block : var (--wa-space-s );
467+ }
468+
469+ : host ([icon-only ]) wa-tab wa-icon {
470+ font-size : var (--wa-font-size-l );
471+ }
472+
473+ : host ([icon-only ]: is ([placement = "start" ], [placement = "end" ])) wa-tab-group ::part (nav ),
474+ : host ([icon-only ]: is ([placement = "start" ], [placement = "end" ])) wa-tab-group ::part (tabs ) {
475+ padding : 0 ;
476+ margin : 0 ;
477+ }
478+
479+ : host ([icon-only ]: is ([placement = "start" ], [placement = "end" ])) wa-tab-group ::part (nav ) {
480+ flex : 0 0 auto;
481+ }
482+
483+ : host ([with-toolbar ]) .nav-toolbar-spacer {
484+ flex : 1 1 auto;
485+ min-height : 0 ;
486+ min-width : 0 ;
487+ pointer-events : none;
488+ }
489+
490+ : host ([with-toolbar ]: is ([placement = "start" ], [placement = "end" ])) wa-tab-group ::part (nav ) {
491+ display : grid;
492+ grid-template-rows : 1fr ;
493+ height : 100% ;
494+ min-height : 0 ;
495+ }
496+
497+ : host ([with-toolbar ]: is ([placement = "start" ], [placement = "end" ])) wa-tab-group ::part (tabs ) {
498+ display : flex;
499+ flex-direction : column;
500+ min-height : 0 ;
501+ height : 100% ;
502+ flex : 1 1 auto;
503+ align-self : stretch;
504+ }
505+
419506 wa-tab-panel {
420507 --padding : 0px ;
421508 }
@@ -431,7 +518,6 @@ export class DocksTabs extends DocksContainer {
431518 justify-content : center;
432519 width : 100% ;
433520 height : 100% ;
434- grid-row : 2 ;
435521 }
436522
437523 .empty-content {
0 commit comments