@jimka/typescript-ui 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/README.md +5 -18
  2. package/dist/lib/AbstractBooleanInput-C6CidMMX.js +2 -0
  3. package/dist/lib/AbstractBooleanInput-C6CidMMX.js.map +1 -0
  4. package/dist/lib/AbstractInput-CfjW_HM2.js +2 -0
  5. package/dist/lib/AbstractInput-CfjW_HM2.js.map +1 -0
  6. package/dist/lib/AnimatedDropdown-BFz-DO7R.js +2 -0
  7. package/dist/lib/AnimatedDropdown-BFz-DO7R.js.map +1 -0
  8. package/dist/lib/Border-5Ki2DCWz.js +2 -0
  9. package/dist/lib/Border-5Ki2DCWz.js.map +1 -0
  10. package/dist/lib/BoxLayout-8ryMD4j5.js +2 -0
  11. package/dist/lib/BoxLayout-8ryMD4j5.js.map +1 -0
  12. package/dist/lib/Button-Cdq2ySPL.js +2 -0
  13. package/dist/lib/Button-Cdq2ySPL.js.map +1 -0
  14. package/dist/lib/Card-AKIUAWYS.js +2 -0
  15. package/dist/lib/Card-AKIUAWYS.js.map +1 -0
  16. package/dist/lib/ComboBox-e_YVsWBr.js +2 -0
  17. package/dist/lib/ComboBox-e_YVsWBr.js.map +1 -0
  18. package/dist/lib/Component-Bs6TvaGZ.js +2 -0
  19. package/dist/lib/Component-Bs6TvaGZ.js.map +1 -0
  20. package/dist/lib/Container-CzyZeMyZ.js +2 -0
  21. package/dist/lib/Container-CzyZeMyZ.js.map +1 -0
  22. package/dist/lib/DOM-DeF_dWe2.js +2 -0
  23. package/dist/lib/DOM-DeF_dWe2.js.map +1 -0
  24. package/dist/lib/DragManager-c6M_mE3e.js +2 -0
  25. package/dist/lib/DragManager-c6M_mE3e.js.map +1 -0
  26. package/dist/lib/FieldDecorator-Bl0h9UEF.js +2 -0
  27. package/dist/lib/FieldDecorator-Bl0h9UEF.js.map +1 -0
  28. package/dist/lib/Fit-CUS1JXy3.js +2 -0
  29. package/dist/lib/Fit-CUS1JXy3.js.map +1 -0
  30. package/dist/lib/Glyph-6Ki3A1_a.js +2 -0
  31. package/dist/lib/Glyph-6Ki3A1_a.js.map +1 -0
  32. package/dist/lib/Grid-D8luwRPI.js +2 -0
  33. package/dist/lib/Grid-D8luwRPI.js.map +1 -0
  34. package/dist/lib/GridConstraints-CzC0w4i-.js +2 -0
  35. package/dist/lib/GridConstraints-CzC0w4i-.js.map +1 -0
  36. package/dist/lib/HBox-BWMf9h6r.js +2 -0
  37. package/dist/lib/HBox-BWMf9h6r.js.map +1 -0
  38. package/dist/lib/Header-CQjomjo0.js +2 -0
  39. package/dist/lib/Header-CQjomjo0.js.map +1 -0
  40. package/dist/lib/IconText-BXJK0MOq.js +2 -0
  41. package/dist/lib/IconText-BXJK0MOq.js.map +1 -0
  42. package/dist/lib/LayoutSerialization-C_zcwaN3.js +2 -0
  43. package/dist/lib/LayoutSerialization-C_zcwaN3.js.map +1 -0
  44. package/dist/lib/LayoutSizes-XdhpAOmc.js +2 -0
  45. package/dist/lib/LayoutSizes-XdhpAOmc.js.map +1 -0
  46. package/dist/lib/List-BgxnLWnl.js +2 -0
  47. package/dist/lib/List-BgxnLWnl.js.map +1 -0
  48. package/dist/lib/Menu-BdnwRY0M.js +2 -0
  49. package/dist/lib/Menu-BdnwRY0M.js.map +1 -0
  50. package/dist/lib/MenuButton-D15cSbMf.js +2 -0
  51. package/dist/lib/MenuButton-D15cSbMf.js.map +1 -0
  52. package/dist/lib/Panel-DApa7vA6.js +2 -0
  53. package/dist/lib/Panel-DApa7vA6.js.map +1 -0
  54. package/dist/lib/Position-DJKB0N3l.js +2 -0
  55. package/dist/lib/Position-DJKB0N3l.js.map +1 -0
  56. package/dist/lib/ProgressSpinner-CNYcsBhw.js +2 -0
  57. package/dist/lib/ProgressSpinner-CNYcsBhw.js.map +1 -0
  58. package/dist/lib/RadioButton-CepkneSA.js +2 -0
  59. package/dist/lib/RadioButton-CepkneSA.js.map +1 -0
  60. package/dist/lib/RovingTabIndex-D-zOuptw.js +2 -0
  61. package/dist/lib/RovingTabIndex-D-zOuptw.js.map +1 -0
  62. package/dist/lib/Scrollbar-DQdCdj94.js +2 -0
  63. package/dist/lib/Scrollbar-DQdCdj94.js.map +1 -0
  64. package/dist/lib/Slider-DvDdVvRm.js +2 -0
  65. package/dist/lib/Slider-DvDdVvRm.js.map +1 -0
  66. package/dist/lib/Spacer-BqiPiCqz.js +2 -0
  67. package/dist/lib/Spacer-BqiPiCqz.js.map +1 -0
  68. package/dist/lib/TabButton-DqYDfN7P.js +2 -0
  69. package/dist/lib/TabButton-DqYDfN7P.js.map +1 -0
  70. package/dist/lib/Text-DJoUHukb.js +2 -0
  71. package/dist/lib/Text-DJoUHukb.js.map +1 -0
  72. package/dist/lib/Tooltip-5BR7yYEX.js +3 -0
  73. package/dist/lib/Tooltip-5BR7yYEX.js.map +1 -0
  74. package/dist/lib/VBox-CdV9VGxR.js +2 -0
  75. package/dist/lib/VBox-CdV9VGxR.js.map +1 -0
  76. package/dist/lib/VirtualRowView-C18j90n5.js +2 -0
  77. package/dist/lib/VirtualRowView-C18j90n5.js.map +1 -0
  78. package/dist/lib/VirtualScroller-BKMvTisA.js +2 -0
  79. package/dist/lib/VirtualScroller-BKMvTisA.js.map +1 -0
  80. package/dist/lib/component/button.es.js +1 -1
  81. package/dist/lib/component/button.es.js.map +1 -1
  82. package/dist/lib/component/chart.es.js +1 -1
  83. package/dist/lib/component/chart.es.js.map +1 -1
  84. package/dist/lib/component/container.es.js +1 -1
  85. package/dist/lib/component/container.es.js.map +1 -1
  86. package/dist/lib/component/diagram.es.js +1 -1
  87. package/dist/lib/component/diagram.es.js.map +1 -1
  88. package/dist/lib/component/display.es.js +1 -1
  89. package/dist/lib/component/display.es.js.map +1 -1
  90. package/dist/lib/component/editor.es.js +3 -1
  91. package/dist/lib/component/editor.es.js.map +1 -1
  92. package/dist/lib/component/input.es.js +1 -1
  93. package/dist/lib/component/input.es.js.map +1 -1
  94. package/dist/lib/component/list.es.js +1 -1
  95. package/dist/lib/component/list.es.js.map +1 -1
  96. package/dist/lib/component/menubar.es.js +1 -1
  97. package/dist/lib/component/menubar.es.js.map +1 -1
  98. package/dist/lib/component/table.es.js +2 -2
  99. package/dist/lib/component/table.es.js.map +1 -1
  100. package/dist/lib/component/tree.es.js +1 -1
  101. package/dist/lib/component/tree.es.js.map +1 -1
  102. package/dist/lib/core.es.js +1 -1
  103. package/dist/lib/core.es.js.map +1 -1
  104. package/dist/lib/layout.es.js +1 -1
  105. package/dist/lib/overlay.es.js +1 -1
  106. package/dist/lib/overlay.es.js.map +1 -1
  107. package/dist/lib/primitive.es.js +1 -1
  108. package/dist/lib/router.es.js +2 -0
  109. package/dist/lib/router.es.js.map +1 -0
  110. package/dist/lib/types/component/container/SplitGutter.d.ts +1 -1
  111. package/dist/lib/types/component/container/WindowBorder.d.ts +1 -1
  112. package/dist/lib/types/component/display/Markdown.d.ts +10 -0
  113. package/dist/lib/types/component/display/index.d.ts +1 -1
  114. package/dist/lib/types/component/editor/MarkdownEditor.d.ts +6 -0
  115. package/dist/lib/types/component/editor/markdownTableTransformer.d.ts +2 -0
  116. package/dist/lib/types/core/DOM.d.ts +6 -0
  117. package/dist/lib/types/overlay/AbstractWindow.d.ts +1 -1
  118. package/dist/lib/types/router/RoutePattern.d.ts +18 -0
  119. package/dist/lib/types/router/Router.d.ts +36 -0
  120. package/dist/lib/types/router/index.d.ts +2 -0
  121. package/dist/lib/validation.es.js +1 -1
  122. package/llms.txt +8 -4
  123. package/package.json +6 -1
@@ -1,2 +1,2 @@
1
- import{n as e,t,u as n}from"../Position-B-WfZYAJ.js";import{f as r,t as i}from"../Component-BR2f0Itb.js";import{t as a}from"../Scrollbar-DchfiFKC.js";import{t as o}from"../Container-3KsvsJ6S.js";import{t as s}from"../Fit-CFIr4P9R.js";import{t as c}from"../Text-D1RrBY52.js";import{t as l}from"../HBox-Q8y1PHlk.js";import{c as u,h as d,m as f,o as p,s as m,u as h}from"../LayoutSizes-BW4wyGDI.js";import{n as g,r as _}from"../Border-oZC_SDy8.js";import{t as v}from"../LayoutConstraints-CHI2lhPd.js";import{n as y,r as b}from"../Menu-Wz4MK6sA.js";import{a as x,i as S,n as C,r as w,t as T}from"../GridConstraints-Q3wvuovN.js";import{t as E}from"../Grid-Czux--Mr.js";import{t as D}from"../Spacer-BVQncB5B.js";import{t as O}from"../VirtualScroller-BPDmO1ir.js";var k=r(class Legend extends c{static LEFT_MARGIN=10;constructor(e){super(void 0,{...e??{},tag:`legend`}),this.setPosition(t.STATIC)}applyStyle(e){return super.applyStyle(e),this.setElementCSSRule(`marginLeft`,`${Legend.LEFT_MARGIN}px`),this}}),A={tag:`fieldset`,border:`var(--ts-ui-input-border)`,borderRadius:`var(--ts-ui-border-radius, 4px)`,insets:new e(5,8,8,8),preferredSize:{width:200,height:200},minSize:{width:100,height:100}},j=class FieldSet extends i{static LEGEND_CLEARANCE_FALLBACK=16;_legend=new k;_legendClearance=null;constructor(e=``,t,n){super(t,{...A,...n??{}}),this._legend.setText(e),this._legend.setDisplayed(e!==``)}applyOptions(e){return super.applyOptions(e),e.legend!==void 0&&this.setTitle(e.legend),this}getTitle(){return this._legend.getText()}setTitle(e){return this._legend.setText(e),this._legend.setDisplayed(e!==``),this.clampLegendWidth(),this}clampLegendWidth(){if(this.getTitle()===``)return this;let e=this.getWidth();if(e<=0)return this;let t=this.getPerimeterSize(),n=t.left+t.right,r=Math.max(0,e-n);return this._legend.setMaxSize(r,Number.MAX_VALUE),this}getMinSize(){if(this.getTitle()===``)return super.getMinSize();let e=super.getMinSize(),t=this._legend.getMinSize();if(!t)return e;let n=this.getPerimeterSize(),r=n.left+n.right,i=t.width+r;return e?{width:Math.max(e.width,i),height:e.height}:{width:i,height:0}}getPerimeterSize(){let e=super.getPerimeterSize();return e.top+=this.legendClearance(),e}legendClearance(){if(this.getTitle()===``)return 0;if(this._legendClearance!=null)return this._legendClearance;if(n.source.isModelled())return FieldSet.LEGEND_CLEARANCE_FALLBACK;let e=this._legend.getElement();if(e&&n.source.isConnected(e)){let t=n.source.getOffsetSize(e).offsetHeight;if(t>0)return this._legendClearance=t,this._legendClearance}return FieldSet.LEGEND_CLEARANCE_FALLBACK}doLayout(){return super.doLayout(),this.clampLegendWidth(),this}render(){let e=super.render();return n.sink.appendChild(e,this._legend.getElement(!0)),e}},M=r(j),N=8,LabeledGrid=class extends o{_columns;_grid;_flowCol=0;_rowCount=0;constructor(e){super(e),this._columns=e?.columns??1,this._grid=new E({baselineAlign:!0,columns:2*this._columns,spacing:e?.fieldSpacing??N,columnTracks:this.buildColumnTracks(),rows:0,rowTracks:[]}),this.setLayoutManager(this._grid),e?.rows&&this.applyRows(e.rows)}addField(e,t){return this.openRow(),this.addComponent(new c(e)),this.addComponent(t),this._flowCol+=2,this._flowCol>=2*this._columns&&(this._flowCol=0),this}addRow(e){this.finishRow();for(let t of e)this.addField(t.title,t.component);return this.finishRow(),this}addFullWidthRow(e){this.finishRow(),this.openRow();let t=new T;return t.colSpan=2*this._columns,this.addComponent(e,t),this}getColumns(){return this._columns}buildColumnTracks(){let e=[];for(let t=0;t<this._columns;t++)e.push({mode:`content`}),e.push({mode:`weight`,value:1});return e}applyRows(e){for(let t of e)Array.isArray(t)?this.addRow(t):this.addFullWidthRow(t.component)}openRow(){this._flowCol===0&&(this._rowCount++,this.growRows())}finishRow(){if(this._flowCol===0)return;let e=2*this._columns-this._flowCol;for(let t=0;t<e;t++)this.addComponent(new i);this._flowCol=0}growRows(){this._grid.setRows(this._rowCount),this._grid.setRowTracks(Array.from({length:this._rowCount},()=>({mode:`content`})))}},P=r(LabeledGrid),F={preferredSize:void 0,minSize:void 0},LabeledFieldSet=class extends j{_labeledGrid;constructor(e=``,t,n){super(e,t,{...F,...n??{}}),this._labeledGrid=new P({columns:t?.columns,fieldSpacing:t?.fieldSpacing,rows:t?.rows}),this.setLayoutManager(new s),this.addComponent(this._labeledGrid)}addField(e,t){return this._labeledGrid.addField(e,t),this}addRow(e){return this._labeledGrid.addRow(e),this}addFullWidthRow(e){return this._labeledGrid.addFullWidthRow(e),this}getColumns(){return this._labeledGrid.getColumns()}getGrid(){return this._labeledGrid}},I=r(LabeledFieldSet),L=22,R=1,z=4,B={insets:new e(0,6,0,6)},StatusBar=class extends o{_message=``;_defaultMessage=``;_messageTimer=null;_messageText;_spacer;constructor(e){super(e,B);let t=new l;t.setComponentSpacing(z),this.setLayoutManager(t),this.setBackgroundColor(`var(--ts-ui-statusbar-bg, rgb(245, 245, 245))`),this.setForegroundColor(`var(--ts-ui-statusbar-color, rgb(60, 60, 60))`),this.setBorder({borderTop:`${R}px solid var(--ts-ui-statusbar-border, rgb(220, 220, 220))`}),this.setMinSize(0,22),this.setMaxSize(2**53-1,22),this._messageText=new c(``),this._messageText.centerInHeight(22-R),this._spacer=D.flex(),this.addComponent(this._messageText),this.addComponent(this._spacer),this.getAria().setRole(`status`),this.getAria().setLive(`polite`),this._options.defaultMessage!==void 0&&(this._defaultMessage=this._options.defaultMessage),this._options.message===void 0?this._defaultMessage!==``&&(this._message=this._defaultMessage,this._messageText.setText(this._defaultMessage)):this.setMessage(this._options.message)}applyOptions(e){return super.applyOptions(e),e.message!==void 0&&(this._options.message=e.message),e.defaultMessage!==void 0&&(this._options.defaultMessage=e.defaultMessage),this}addLeft(e){return this.insertComponent(e,this.getComponents().indexOf(this._spacer)),this}addRight(e){return this.addComponent(e),this}removeLeft(e){return this.removeComponent(e),this}removeRight(e){return this.removeComponent(e),this}setMessage(e,t){return this._messageTimer!==null&&(clearTimeout(this._messageTimer),this._messageTimer=null),this._message=e,this._messageText.setText(e),t!==void 0&&t>0&&(this._messageTimer=setTimeout(()=>{this._messageTimer=null,this._message=this._defaultMessage,this._messageText.setText(this._defaultMessage)},t)),this}getMessage(){return this._message}clearMessage(){return this.setMessage(this._defaultMessage)}setDefaultMessage(e){return this._defaultMessage=e,this._messageTimer===null&&(this._message=e,this._messageText.setText(e)),this}getDefaultMessage(){return this._defaultMessage}destructor(){this._messageTimer!==null&&(clearTimeout(this._messageTimer),this._messageTimer=null),super.destructor()}},V=r(StatusBar),TabPanel=class extends o{constructor(e){if(super(e),this.setLayoutManager(new p(e?.tabOptions)),e?.tabs)for(let t of e.tabs)this.addTab(t.component,t.label,{closeable:t.closeable,glyph:t.glyph});e?.onTabClose&&this.getTab().on(`tabclose`,e.onTabClose)}addTab(e,t,n){let r=new v;return r.name=t,r.closeable=n?.closeable??!1,r.glyph=n?.glyph??null,this.addComponent(e,r),this}addLazyTab(e,t,n){let r=new v;return r.name=t,r.closeable=n?.closeable??!1,r.glyph=n?.glyph??null,this.getTab().addLazyTab(e,t,r),this}getTab(){return this.getLayoutManager()}},H=r(TabPanel),AccordionPanel=class extends o{constructor(e){if(super(e),this.setLayoutManager(new w),e?.singleOpen!==void 0&&this.getAccordion().setSingleOpen(e.singleOpen),e?.resizable!==void 0&&this.getAccordion().setResizable(e.resizable),e?.sections)for(let t of e.sections)this.addSection(t.component,t.label,t.initiallyOpen,t.glyph,t.tools,t.weight);e?.onSectionToggle&&this.getAccordion().on(`sectiontoggle`,e.onSectionToggle)}addSection(e,t,n,r,i,a){let o=new C(t,n,r,i);return a!==void 0&&(o.weight=a),this.addComponent(e,o),this}getAccordion(){return this.getLayoutManager()}},U=r(AccordionPanel);export{S as AccordionHeader,x as AccordionIndicator,U as AccordionPanel,_ as CollapseButton,f as Direction,M as FieldSet,I as LabeledFieldSet,P as LabeledGrid,k as Legend,b as MenuItem,y as MenuSeparator,L as STATUS_BAR_HEIGHT,u as ScrollStrip,a as Scrollbar,D as Spacer,g as SplitGutter,V as StatusBar,m as TabBar,H as TabPanel,O as VirtualScroller,d as WindowBorder,h as WindowHeader};
1
+ import{t as e}from"../DOM-DeF_dWe2.js";import{n as t,t as n}from"../Position-DJKB0N3l.js";import{f as r,t as i}from"../Component-Bs6TvaGZ.js";import{t as a}from"../Scrollbar-DQdCdj94.js";import{t as o}from"../Container-CzyZeMyZ.js";import{t as s}from"../Fit-CUS1JXy3.js";import{t as c}from"../Text-DJoUHukb.js";import{t as l}from"../HBox-BWMf9h6r.js";import{c as u,h as d,m as f,o as p,s as m,u as h}from"../LayoutSizes-XdhpAOmc.js";import{n as g,r as _}from"../Border-5Ki2DCWz.js";import{t as v}from"../LayoutConstraints-CHI2lhPd.js";import{n as y,r as b}from"../Menu-BdnwRY0M.js";import{a as x,i as S,n as C,r as w,t as T}from"../GridConstraints-CzC0w4i-.js";import{t as E}from"../Grid-D8luwRPI.js";import{t as D}from"../Spacer-BqiPiCqz.js";import{t as O}from"../VirtualScroller-BKMvTisA.js";var k=r(class Legend extends c{static LEFT_MARGIN=10;constructor(e){super(void 0,{...e??{},tag:`legend`}),this.setPosition(n.STATIC)}applyStyle(e){return super.applyStyle(e),this.setElementCSSRule(`marginLeft`,`${Legend.LEFT_MARGIN}px`),this}}),A={tag:`fieldset`,border:`var(--ts-ui-input-border)`,borderRadius:`var(--ts-ui-border-radius, 4px)`,insets:new t(5,8,8,8),preferredSize:{width:200,height:200},minSize:{width:100,height:100}},j=class FieldSet extends i{static LEGEND_CLEARANCE_FALLBACK=16;_legend=new k;_legendClearance=null;constructor(e=``,t,n){super(t,{...A,...n??{}}),this._legend.setText(e),this._legend.setDisplayed(e!==``)}applyOptions(e){return super.applyOptions(e),e.legend!==void 0&&this.setTitle(e.legend),this}getTitle(){return this._legend.getText()}setTitle(e){return this._legend.setText(e),this._legend.setDisplayed(e!==``),this.clampLegendWidth(),this}clampLegendWidth(){if(this.getTitle()===``)return this;let e=this.getWidth();if(e<=0)return this;let t=this.getPerimeterSize(),n=t.left+t.right,r=Math.max(0,e-n);return this._legend.setMaxSize(r,Number.MAX_VALUE),this}getMinSize(){if(this.getTitle()===``)return super.getMinSize();let e=super.getMinSize(),t=this._legend.getMinSize();if(!t)return e;let n=this.getPerimeterSize(),r=n.left+n.right,i=t.width+r;return e?{width:Math.max(e.width,i),height:e.height}:{width:i,height:0}}getPerimeterSize(){let e=super.getPerimeterSize();return e.top+=this.legendClearance(),e}legendClearance(){if(this.getTitle()===``)return 0;if(this._legendClearance!=null)return this._legendClearance;if(e.source.isModelled())return FieldSet.LEGEND_CLEARANCE_FALLBACK;let t=this._legend.getElement();if(t&&e.source.isConnected(t)){let n=e.source.getOffsetSize(t).offsetHeight;if(n>0)return this._legendClearance=n,this._legendClearance}return FieldSet.LEGEND_CLEARANCE_FALLBACK}doLayout(){return super.doLayout(),this.clampLegendWidth(),this}render(){let t=super.render();return e.sink.appendChild(t,this._legend.getElement(!0)),t}},M=r(j),N=8,LabeledGrid=class extends o{_columns;_grid;_flowCol=0;_rowCount=0;constructor(e){super(e),this._columns=e?.columns??1,this._grid=new E({baselineAlign:!0,columns:2*this._columns,spacing:e?.fieldSpacing??N,columnTracks:this.buildColumnTracks(),rows:0,rowTracks:[]}),this.setLayoutManager(this._grid),e?.rows&&this.applyRows(e.rows)}addField(e,t){return this.openRow(),this.addComponent(new c(e)),this.addComponent(t),this._flowCol+=2,this._flowCol>=2*this._columns&&(this._flowCol=0),this}addRow(e){this.finishRow();for(let t of e)this.addField(t.title,t.component);return this.finishRow(),this}addFullWidthRow(e){this.finishRow(),this.openRow();let t=new T;return t.colSpan=2*this._columns,this.addComponent(e,t),this}getColumns(){return this._columns}buildColumnTracks(){let e=[];for(let t=0;t<this._columns;t++)e.push({mode:`content`}),e.push({mode:`weight`,value:1});return e}applyRows(e){for(let t of e)Array.isArray(t)?this.addRow(t):this.addFullWidthRow(t.component)}openRow(){this._flowCol===0&&(this._rowCount++,this.growRows())}finishRow(){if(this._flowCol===0)return;let e=2*this._columns-this._flowCol;for(let t=0;t<e;t++)this.addComponent(new i);this._flowCol=0}growRows(){this._grid.setRows(this._rowCount),this._grid.setRowTracks(Array.from({length:this._rowCount},()=>({mode:`content`})))}},P=r(LabeledGrid),F={preferredSize:void 0,minSize:void 0},LabeledFieldSet=class extends j{_labeledGrid;constructor(e=``,t,n){super(e,t,{...F,...n??{}}),this._labeledGrid=new P({columns:t?.columns,fieldSpacing:t?.fieldSpacing,rows:t?.rows}),this.setLayoutManager(new s),this.addComponent(this._labeledGrid)}addField(e,t){return this._labeledGrid.addField(e,t),this}addRow(e){return this._labeledGrid.addRow(e),this}addFullWidthRow(e){return this._labeledGrid.addFullWidthRow(e),this}getColumns(){return this._labeledGrid.getColumns()}getGrid(){return this._labeledGrid}},I=r(LabeledFieldSet),L=22,R=1,z=4,B={insets:new t(0,6,0,6)},StatusBar=class extends o{_message=``;_defaultMessage=``;_messageTimer=null;_messageText;_spacer;constructor(e){super(e,B);let t=new l;t.setComponentSpacing(z),this.setLayoutManager(t),this.setBackgroundColor(`var(--ts-ui-statusbar-bg, rgb(245, 245, 245))`),this.setForegroundColor(`var(--ts-ui-statusbar-color, rgb(60, 60, 60))`),this.setBorder({borderTop:`${R}px solid var(--ts-ui-statusbar-border, rgb(220, 220, 220))`}),this.setMinSize(0,22),this.setMaxSize(2**53-1,22),this._messageText=new c(``),this._messageText.centerInHeight(22-R),this._spacer=D.flex(),this.addComponent(this._messageText),this.addComponent(this._spacer),this.getAria().setRole(`status`),this.getAria().setLive(`polite`),this._options.defaultMessage!==void 0&&(this._defaultMessage=this._options.defaultMessage),this._options.message===void 0?this._defaultMessage!==``&&(this._message=this._defaultMessage,this._messageText.setText(this._defaultMessage)):this.setMessage(this._options.message)}applyOptions(e){return super.applyOptions(e),e.message!==void 0&&(this._options.message=e.message),e.defaultMessage!==void 0&&(this._options.defaultMessage=e.defaultMessage),this}addLeft(e){return this.insertComponent(e,this.getComponents().indexOf(this._spacer)),this}addRight(e){return this.addComponent(e),this}removeLeft(e){return this.removeComponent(e),this}removeRight(e){return this.removeComponent(e),this}setMessage(e,t){return this._messageTimer!==null&&(clearTimeout(this._messageTimer),this._messageTimer=null),this._message=e,this._messageText.setText(e),t!==void 0&&t>0&&(this._messageTimer=setTimeout(()=>{this._messageTimer=null,this._message=this._defaultMessage,this._messageText.setText(this._defaultMessage)},t)),this}getMessage(){return this._message}clearMessage(){return this.setMessage(this._defaultMessage)}setDefaultMessage(e){return this._defaultMessage=e,this._messageTimer===null&&(this._message=e,this._messageText.setText(e)),this}getDefaultMessage(){return this._defaultMessage}destructor(){this._messageTimer!==null&&(clearTimeout(this._messageTimer),this._messageTimer=null),super.destructor()}},V=r(StatusBar),TabPanel=class extends o{constructor(e){if(super(e),this.setLayoutManager(new p(e?.tabOptions)),e?.tabs)for(let t of e.tabs)this.addTab(t.component,t.label,{closeable:t.closeable,glyph:t.glyph});e?.onTabClose&&this.getTab().on(`tabclose`,e.onTabClose)}addTab(e,t,n){let r=new v;return r.name=t,r.closeable=n?.closeable??!1,r.glyph=n?.glyph??null,this.addComponent(e,r),this}addLazyTab(e,t,n){let r=new v;return r.name=t,r.closeable=n?.closeable??!1,r.glyph=n?.glyph??null,this.getTab().addLazyTab(e,t,r),this}getTab(){return this.getLayoutManager()}},H=r(TabPanel),AccordionPanel=class extends o{constructor(e){if(super(e),this.setLayoutManager(new w),e?.singleOpen!==void 0&&this.getAccordion().setSingleOpen(e.singleOpen),e?.resizable!==void 0&&this.getAccordion().setResizable(e.resizable),e?.sections)for(let t of e.sections)this.addSection(t.component,t.label,t.initiallyOpen,t.glyph,t.tools,t.weight);e?.onSectionToggle&&this.getAccordion().on(`sectiontoggle`,e.onSectionToggle)}addSection(e,t,n,r,i,a){let o=new C(t,n,r,i);return a!==void 0&&(o.weight=a),this.addComponent(e,o),this}getAccordion(){return this.getLayoutManager()}},U=r(AccordionPanel);export{S as AccordionHeader,x as AccordionIndicator,U as AccordionPanel,_ as CollapseButton,f as Direction,M as FieldSet,I as LabeledFieldSet,P as LabeledGrid,k as Legend,b as MenuItem,y as MenuSeparator,L as STATUS_BAR_HEIGHT,u as ScrollStrip,a as Scrollbar,D as Spacer,g as SplitGutter,V as StatusBar,m as TabBar,H as TabPanel,O as VirtualScroller,d as WindowBorder,h as WindowHeader};
2
2
  //# sourceMappingURL=container.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"container.es.js","names":[],"sources":["../../../src/typescript/lib/component/container/Legend.ts","../../../src/typescript/lib/component/container/FieldSet.ts","../../../src/typescript/lib/component/container/LabeledGrid.ts","../../../src/typescript/lib/component/container/LabeledFieldSet.ts","../../../src/typescript/lib/component/container/StatusBar.ts","../../../src/typescript/lib/component/container/TabPanel.ts","../../../src/typescript/lib/component/container/AccordionPanel.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Text, TextOptions } from \"~/component/input/Text.js\"\nimport { Position } from \"~/primitive/Position.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport type { Handle } from \"~/core/DOM.js\";\n\n/**\n * Construction-time options for {@link Legend}.\n *\n * @category Components\n */\nexport interface LegendOptions extends TextOptions {\n}\n\n/**\n * A legend component backed by a `<legend>` element.\n *\n * Uses static CSS positioning so the browser can render the title text inside a fieldset border.\n *\n * @category Components\n */\nclass Legend extends Text<LegendOptions> {\n\n /** Left inset (px) so the title clears the fieldset's left border corner. */\n private static readonly LEFT_MARGIN = 10;\n\n constructor(options?: LegendOptions) {\n // `tag` is structural — the element type is by definition `<legend>`.\n super(undefined, {\n ...(options ?? {}),\n tag: \"legend\",\n });\n\n // `<legend>` only renders inside its fieldset's notch when positioned\n // statically — the framework's \"every component is absolute\" rule\n // would float the legend out of the border. Documented HTML-semantics\n // exception alongside `FIXED` floating overlays. See\n // ARCHITECTURE.md §Positioning.\n this.setPosition(Position.STATIC);\n }\n\n /**\n * Applies base styles, then re-asserts a left margin so the legend clears\n * the fieldset's left border corner instead of hugging it. `Component`'s\n * `applyStyle` zeroes the `margin` shorthand on every component, which\n * strips the browser's default `<legend>` inset; this longhand write runs\n * after `super` and overrides only the left side.\n *\n * @param element - The element handle to apply styles to.\n */\n applyStyle(element: Handle): this {\n super.applyStyle(element);\n\n this.setElementCSSRule(\"marginLeft\", `${Legend.LEFT_MARGIN}px`);\n\n return this;\n }\n}\n\nconst LegendCallable = callable(Legend);\ntype LegendCallable = Legend;\nexport {\n Legend as _Legend,\n LegendCallable as Legend\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport { Legend } from \"~/component/container/Legend.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { Size } from \"~/primitive/Size.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Construction-time options for {@link FieldSet}.\n *\n * @category Components\n */\nexport interface FieldSetOptions extends ComponentOptions {\n legend?: string;\n}\n\n/**\n * Subclass defaults layered into `Component._defaultOptions` via the second\n * super arg. Any field the caller omits falls back to one of these values via\n * the getters / `applyStyle`, which consult `_defaultOptions` directly — the\n * default is never dispatched into `_options`.\n */\nconst _defaultFieldSetOptions: Partial<FieldSetOptions> = {\n tag: \"fieldset\",\n border: \"var(--ts-ui-input-border)\",\n borderRadius: \"var(--ts-ui-border-radius, 4px)\",\n // Intrinsic chrome carried as insets, not CSS padding. Top (5) is the gap\n // above content; the legend's own reserved height is added separately in\n // getPerimeterSize so it counts toward the box height without also pushing\n // the child origin (a <fieldset> already offsets its absolutely positioned\n // children below the legend). Sides/bottom (8) are the inner gutter.\n insets: new Insets(5, 8, 8, 8),\n preferredSize: { width: 200, height: 200 },\n minSize: { width: 100, height: 100 },\n};\n\n/**\n * A fieldset component with an embedded legend title.\n *\n * Renders a `<fieldset>` element and prepends a Legend child for the group title.\n *\n * @category Components\n */\nclass FieldSet extends Component {\n\n /** Legend clearance (px) used before the legend element can be measured. */\n private static readonly LEGEND_CLEARANCE_FALLBACK = 16;\n\n private _legend: Legend = new Legend();\n\n /** Cached measured legend height; null until a positive measurement lands. */\n private _legendClearance: number | null = null;\n\n constructor(title: string = \"\", options?: FieldSetOptions, subclassDefaults?: Partial<FieldSetOptions>) {\n super(options, { ..._defaultFieldSetOptions, ...(subclassDefaults ?? {}) });\n\n this._legend.setText(title);\n this._legend.setDisplayed(title !== \"\");\n }\n\n /**\n * Applies a {@link FieldSetOptions} bag, dispatching the legend title text\n * after inherited Component fields.\n *\n * @param options - The options bag carrying the values to apply.\n */\n protected applyOptions(options: FieldSetOptions): this {\n super.applyOptions(options);\n\n if (options.legend !== undefined) {\n this.setTitle(options.legend);\n }\n\n return this;\n }\n\n /**\n * Returns the fieldset legend title text.\n *\n * @returns The current legend text string.\n */\n getTitle() {\n return this._legend.getText();\n }\n\n /**\n * Sets the fieldset legend title text.\n *\n * @param title - The text to display in the legend.\n */\n setTitle(title: string) : this {\n this._legend.setText(title);\n this._legend.setDisplayed(title !== \"\");\n this.clampLegendWidth();\n\n return this;\n }\n\n /**\n * Clamps the legend's `max-width` to the fieldset's current inner width so\n * a long title ellipsises inside the border notch instead of spilling out.\n * The legend already carries `overflow:hidden; text-overflow:ellipsis;\n * white-space:nowrap` (from `Text`'s `truncate:true`), so a `max-width` is\n * the only missing piece for the ellipsis to engage.\n *\n * @returns This component, for method chaining.\n *\n * @remarks No-op until the first layout pass commits a width — `getWidth()`\n * returns 0 on a detached / unsized fieldset. `doLayout()` re-runs the\n * clamp once a width exists.\n */\n private clampLegendWidth(): this {\n if (this.getTitle() === \"\") {\n return this;\n }\n\n const width = this.getWidth();\n if (width <= 0) {\n return this;\n }\n\n const perim = this.getPerimeterSize();\n const chromeW = perim.left + perim.right;\n const innerW = Math.max(0, width - chromeW);\n\n this._legend.setMaxSize(innerW, Number.MAX_VALUE);\n\n return this;\n }\n\n /**\n * Returns the minimum size, augmented to include the legend's measured\n * width so the legend never spills out of the border notch. The legend is\n * rendered statically by the browser (it's not in the framework layout\n * tree), so `super.getMinSize()` from the layout manager doesn't see it.\n *\n * @returns The minimum `{width, height}`, ensuring the legend's text fits.\n */\n getMinSize(): Size | null {\n if (this.getTitle() === \"\") {\n return super.getMinSize();\n }\n\n const baseMin = super.getMinSize();\n const legendMin = this._legend.getMinSize();\n if (!legendMin) {\n return baseMin;\n }\n\n const perim = this.getPerimeterSize();\n const chromeW = perim.left + perim.right;\n\n const fieldsetW = legendMin.width + chromeW;\n if (!baseMin) {\n return { width: fieldsetW, height: 0 };\n }\n\n return {\n width: Math.max(baseMin.width, fieldsetW),\n height: baseMin.height,\n };\n }\n\n /**\n * Augments the top perimeter with the legend's reserved vertical space.\n *\n * A `<fieldset>` reserves room for its `<legend>` at the top of its content\n * box automatically: absolutely positioned children begin below the legend\n * regardless of the framework's insets, and the browser supplies that\n * offset itself. {@link getInnerSize} must account for the same space or\n * the bottom row overflows the border, so this adds the legend's height to\n * the `top` perimeter. The child origin is deliberately left untouched —\n * the content-inset origin derives from insets alone, and the browser\n * already provides the matching downward offset, so adding it to the\n * origin too would double-count it.\n *\n * The legend is browser-rendered chrome, not a framework-layout child, so\n * its height is read from the element and cached. The measurement slightly\n * over-reserves when the legend is raised into the border notch, which errs\n * toward extra bottom space rather than clipping. Before the element can be\n * measured a constant fallback is used and not cached, so the real height\n * supersedes it on the first connected pass.\n *\n * @returns The base perimeter with the legend clearance added to `top`.\n */\n getPerimeterSize() {\n const perim = super.getPerimeterSize();\n\n perim.top += this.legendClearance();\n\n return perim;\n }\n\n /**\n * Returns the legend's reserved top clearance in pixels: the measured\n * legend height once it is connected and laid out (cached after the first\n * positive measurement), or {@link LEGEND_CLEARANCE_FALLBACK} until then.\n *\n * @returns The legend clearance in pixels.\n */\n private legendClearance(): number {\n if (this.getTitle() === \"\") {\n return 0;\n }\n\n if (this._legendClearance != null) {\n return this._legendClearance;\n }\n\n // A modelled (no-browser) source has no native `<legend>` box to\n // measure, so short-circuit to the fallback rather than reading\n // `offsetHeight`, which is 0 or unavailable offline.\n if (DOM.source.isModelled()) {\n return FieldSet.LEGEND_CLEARANCE_FALLBACK;\n }\n\n const element = this._legend.getElement();\n\n if (element && DOM.source.isConnected(element)) {\n const offsetHeight = DOM.source.getOffsetSize(element).offsetHeight;\n\n if (offsetHeight > 0) {\n this._legendClearance = offsetHeight;\n\n return this._legendClearance;\n }\n }\n\n return FieldSet.LEGEND_CLEARANCE_FALLBACK;\n }\n\n /**\n * Lays out the fieldset, then re-clamps the legend to the freshly committed\n * inner width so the title ellipsis tracks fieldset resizes. The clamp runs\n * post-layout because `getWidth()` only reports a real width once the layout\n * pass has committed one.\n *\n * @returns This component, for method chaining.\n */\n doLayout(): this {\n super.doLayout();\n this.clampLegendWidth();\n\n return this;\n }\n\n /**\n * Renders the fieldset element and appends the legend as its first child.\n *\n * @returns The created HTMLFieldSetElement with the legend prepended.\n */\n render() {\n let element = super.render();\n\n DOM.sink.appendChild(element, this._legend.getElement(true)!);\n\n return element;\n }\n}\n\nconst FieldSetCallable = callable(FieldSet);\ntype FieldSetCallable = FieldSet;\nexport {\n FieldSet as _FieldSet,\n FieldSetCallable as FieldSet\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { Container, ContainerOptions } from \"~/core/Container.js\";\nimport { Grid } from \"~/layout/Grid.js\";\nimport { GridConstraints } from \"~/layout/GridConstraints.js\";\nimport { GridTrack } from \"~/layout/GridTrack.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Default inter-cell spacing (px) for the internal form grid. Mirrors the\n * hand-rolled Binding demo's `spacing: 8` so a {@link LabeledGrid} matches the\n * reference labelled-form look without the caller restating it.\n */\nconst FIELD_SPACING_DEFAULT = 8;\n\n/**\n * A title/field pair: a label and the input it labels.\n *\n * @category Components\n */\nexport interface LabeledFieldDescriptor {\n\n /** Label text rendered as a baseline-aligned `Text`. */\n title: string;\n\n /** The input/component placed beside the label. */\n component: Component;\n}\n\n/**\n * One row of a {@link LabeledGrid}: either an array of pairs (one per logical\n * column, left-to-right; a short array leaves trailing columns empty), or a\n * single component that spans every column (status lines, button bars).\n *\n * @category Components\n */\nexport type LabeledRowDescriptor =\n | LabeledFieldDescriptor[]\n | { component: Component; fullWidth: true };\n\n/**\n * Construction-time options for {@link LabeledGrid}.\n *\n * @category Components\n */\nexport interface LabeledGridOptions extends ContainerOptions {\n\n /** Logical title/field columns laid side by side. Default `1`. */\n columns?: number;\n\n /** Inter-cell spacing in px for the grid. Default mirrors the demo's `8`. */\n fieldSpacing?: number;\n\n /** Declarative rows, applied in order at construction via the same path as `addRow`. */\n rows?: LabeledRowDescriptor[];\n}\n\n/**\n * A chrome-less baseline-aligned title/field grid, rendered in a plain `<div>`\n * with no border and no legend. {@link LabeledFieldSet} composes one of these\n * inside its `<fieldset>` chrome; use `LabeledGrid` directly wherever the\n * baseline-aligned form layout is wanted without the fieldset border/legend.\n *\n * The body is a single [`Grid`](/api/layout/classes/Grid) in\n * `baselineAlign` mode with `2 × columns` grid-columns: each logical column is\n * a content-sized title track followed by a weight-sized input track, so titles\n * hug their text while inputs share a common right edge per column. Pairs flow\n * left-to-right and wrap to a new row when the current one fills; a full-width\n * row spans every column via [`GridConstraints`](/api/layout/classes/GridConstraints)\n * `colSpan`.\n *\n * @category Components\n */\nclass LabeledGrid extends Container<LabeledGridOptions> {\n\n /** Logical title/field column count (the grid has `2 ×` this many grid-columns). */\n private _columns: number;\n\n /** The internal baseline grid; its `rows`/`rowTracks` grow as rows are added. */\n private _grid: Grid;\n\n /** Next free grid-column on the current flow row (0-based, in grid-columns). */\n private _flowCol: number = 0;\n\n /** Running grid-row count, pushed into the grid's `rows` + `rowTracks`. */\n private _rowCount: number = 0;\n\n /**\n * Builds the grid, installs the internal baseline grid, and replays any\n * declarative `rows`.\n *\n * @param options - Grid structure: `columns`, `fieldSpacing`, `rows`.\n */\n constructor(options?: LabeledGridOptions) {\n super(options);\n\n this._columns = options?.columns ?? 1;\n\n this._grid = new Grid({\n baselineAlign: true,\n columns: 2 * this._columns,\n spacing: options?.fieldSpacing ?? FIELD_SPACING_DEFAULT,\n columnTracks: this.buildColumnTracks(),\n rows: 0,\n rowTracks: [],\n });\n\n this.setLayoutManager(this._grid);\n\n if (options?.rows) {\n this.applyRows(options.rows);\n }\n }\n\n /**\n * Appends one title/field pair into the next free logical column, flowing to\n * a new row when the current one fills.\n *\n * @param title - Label text placed in the content (title) column.\n * @param component - The input placed in the weight (field) column.\n * @returns This component, for method chaining.\n */\n addField(title: string, component: Component): this {\n this.openRow();\n\n this.addComponent(new Text(title));\n this.addComponent(component);\n\n this._flowCol += 2;\n\n if (this._flowCol >= 2 * this._columns) {\n this._flowCol = 0;\n }\n\n return this;\n }\n\n /**\n * Appends a full row of pairs (one per logical column; trailing columns are\n * left empty for a short array).\n *\n * @param fields - The pairs to place across the row, left-to-right.\n * @returns This component, for method chaining.\n */\n addRow(fields: LabeledFieldDescriptor[]): this {\n this.finishRow();\n\n for (const field of fields) {\n this.addField(field.title, field.component);\n }\n\n this.finishRow();\n\n return this;\n }\n\n /**\n * Appends a component spanning every column on its own row.\n *\n * @param component - The component to span the full grid width.\n * @returns This component, for method chaining.\n */\n addFullWidthRow(component: Component): this {\n this.finishRow();\n this.openRow();\n\n const constraints = new GridConstraints();\n constraints.colSpan = 2 * this._columns;\n\n this.addComponent(component, constraints);\n\n return this;\n }\n\n /**\n * Returns the configured logical column count.\n *\n * @returns The number of side-by-side title/field columns.\n */\n getColumns(): number {\n return this._columns;\n }\n\n /**\n * Builds the `2 × columns` grid-column tracks: for each logical column a\n * content-sized title track (hugs its text) followed by a weight-sized input\n * track (takes the slack so inputs share a right edge).\n *\n * @returns The column tracks for the internal grid.\n */\n private buildColumnTracks(): GridTrack[] {\n const tracks: GridTrack[] = [];\n\n for (let column = 0; column < this._columns; column++) {\n tracks.push({ mode: \"content\" });\n tracks.push({ mode: \"weight\", value: 1 });\n }\n\n return tracks;\n }\n\n /**\n * Replays the declarative `rows` option through the same add path as the\n * imperative API, so construction and runtime calls share one code path.\n *\n * @param rows - The declarative row descriptors to apply, in order.\n */\n private applyRows(rows: LabeledRowDescriptor[]): void {\n for (const row of rows) {\n if (Array.isArray(row)) {\n this.addRow(row);\n } else {\n this.addFullWidthRow(row.component);\n }\n }\n }\n\n /**\n * Opens a new flow row when the first cell of one is about to be added,\n * bumping the running row count and growing the grid so its baseline\n * auto-flow does not stop short of the new row.\n */\n private openRow(): void {\n if (this._flowCol === 0) {\n this._rowCount++;\n this.growRows();\n }\n }\n\n /**\n * Pads any partially-filled flow row with empty spacer cells so the next row\n * starts at grid-column 0. The spacers carry no preferred size, contributing\n * 0 to the grid's content measurement.\n */\n private finishRow(): void {\n if (this._flowCol === 0) {\n return;\n }\n\n const remaining = 2 * this._columns - this._flowCol;\n\n for (let cell = 0; cell < remaining; cell++) {\n this.addComponent(new Component());\n }\n\n this._flowCol = 0;\n }\n\n /**\n * Re-sizes the grid's row count and row tracks to the running row total.\n * Every row track is `content`-sized so inputs keep their natural height.\n */\n private growRows(): void {\n this._grid.setRows(this._rowCount);\n this._grid.setRowTracks(Array.from({ length: this._rowCount }, () => ({ mode: \"content\" as const })));\n }\n}\n\nconst LabeledGridCallable = callable(LabeledGrid);\ntype LabeledGridCallable = LabeledGrid;\nexport {\n LabeledGrid as _LabeledGrid,\n LabeledGridCallable as LabeledGrid\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { Fit } from \"~/layout/Fit.js\";\nimport { LabeledGrid, LabeledGridOptions, LabeledFieldDescriptor } from \"~/component/container/LabeledGrid.js\";\nimport { _FieldSet, FieldSetOptions } from \"~/component/container/FieldSet.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Construction-time options for {@link LabeledFieldSet}.\n *\n * @category Components\n */\nexport interface LabeledFieldSetOptions extends FieldSetOptions, LabeledGridOptions {}\n\n/**\n * Subclass defaults layered under the caller's options. Clears the base\n * FieldSet's fixed 200x200 preferred size and fixed 100x100 min size: a\n * labeled fieldset is exactly as large as its computed grid content, so the\n * fixed values would otherwise pad a short form with dead space (preferred)\n * or force a min floor no tiny form needs (min). With both cleared,\n * `Component.getPreferredSize` / `getMinSize` fall through to the internal\n * grid (forwarded through the `Fit` layout), whose reports already add the\n * legend clearance and insets.\n */\nconst _defaultLabeledFieldSetOptions: Partial<LabeledFieldSetOptions> = {\n preferredSize: undefined,\n minSize: undefined,\n};\n\n/**\n * A {@link FieldSet} whose content is a baseline-aligned form of title/field\n * pairs, generalising the hand-rolled labelled-form pattern from the Binding\n * demo into a reusable container.\n *\n * `LabeledFieldSet` composes a {@link LabeledGrid} inside its `<fieldset>`\n * chrome via a `Fit` layout: `LabeledFieldSet` = a `LabeledGrid` inside a\n * `FieldSet`. Use a bare `LabeledGrid` directly when the baseline-aligned\n * form layout is wanted without the fieldset border/legend.\n *\n * @category Components\n */\nclass LabeledFieldSet extends _FieldSet {\n\n /** The internal chrome-less grid this fieldset composes. */\n private _labeledGrid: LabeledGrid;\n\n /**\n * Builds the fieldset chrome and installs an internal {@link LabeledGrid}\n * via a `Fit` layout.\n *\n * @param title - The legend title (forwarded to {@link FieldSet}).\n * @param options - Form structure: `columns`, `fieldSpacing`, `rows`.\n * @param subclassDefaults - Defaults a subclass layers under the caller's options.\n */\n constructor(title: string = \"\", options?: LabeledFieldSetOptions, subclassDefaults?: Partial<LabeledFieldSetOptions>) {\n super(title, options, { ..._defaultLabeledFieldSetOptions, ...(subclassDefaults ?? {}) });\n\n this._labeledGrid = new LabeledGrid({\n columns: options?.columns,\n fieldSpacing: options?.fieldSpacing,\n rows: options?.rows,\n });\n\n this.setLayoutManager(new Fit());\n this.addComponent(this._labeledGrid);\n }\n\n /**\n * Appends one title/field pair into the next free logical column, flowing to\n * a new row when the current one fills.\n *\n * @param title - Label text placed in the content (title) column.\n * @param component - The input placed in the weight (field) column.\n * @returns This component, for method chaining.\n */\n addField(title: string, component: Component): this {\n this._labeledGrid.addField(title, component);\n\n return this;\n }\n\n /**\n * Appends a full row of pairs (one per logical column; trailing columns are\n * left empty for a short array).\n *\n * @param fields - The pairs to place across the row, left-to-right.\n * @returns This component, for method chaining.\n */\n addRow(fields: LabeledFieldDescriptor[]): this {\n this._labeledGrid.addRow(fields);\n\n return this;\n }\n\n /**\n * Appends a component spanning every column on its own row.\n *\n * @param component - The component to span the full form width.\n * @returns This component, for method chaining.\n */\n addFullWidthRow(component: Component): this {\n this._labeledGrid.addFullWidthRow(component);\n\n return this;\n }\n\n /**\n * Returns the configured logical column count.\n *\n * @returns The number of side-by-side title/field columns.\n */\n getColumns(): number {\n return this._labeledGrid.getColumns();\n }\n\n /**\n * Typed accessor for the internally-composed {@link LabeledGrid}. Use it\n * to inspect the grid's cells directly (e.g. `getGrid().getComponents()`).\n *\n * @returns The wrapped `LabeledGrid` instance.\n */\n getGrid(): LabeledGrid {\n return this._labeledGrid;\n }\n}\n\nconst LabeledFieldSetCallable = callable(LabeledFieldSet);\ntype LabeledFieldSetCallable = LabeledFieldSet;\nexport {\n LabeledFieldSet as _LabeledFieldSet,\n LabeledFieldSetCallable as LabeledFieldSet\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { Container, ContainerOptions } from \"~/core/Container.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { HBox } from \"~/layout/HBox.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { Spacer } from \"./Spacer\";\n\n/**\n * Fixed strip height for every `StatusBar` instance. Kept in lockstep with the\n * `--ts-ui-statusbar-height` token in [Theme.ts](/api/core/interfaces/Theme);\n * changing one without the other lets the bar grow taller than its chrome\n * background suggests.\n */\nexport const STATUS_BAR_HEIGHT: number = 22;\n\n/**\n * Pixel width of the bar's top separator border. Hardcoded rather than\n * read from the theme because the value is part of the bar's box-model\n * arithmetic — it determines the inner content height\n * (`STATUS_BAR_HEIGHT - STATUS_BAR_BORDER_TOP_WIDTH`) that the message\n * text line-height must match for vertical centring. A theme that wanted\n * a thicker rule would have to bump `STATUS_BAR_HEIGHT` in lockstep.\n */\nconst STATUS_BAR_BORDER_TOP_WIDTH: number = 1;\n\n/**\n * Gap between adjacent status-bar widgets. Carries over the spacing the\n * former per-zone `HBox`es used; the outer row previously used 0 because\n * the zones butted against the flex spacer, which absorbs any gap anyway.\n */\nconst STATUS_BAR_SPACING: number = 4;\n\n/**\n * Construction-time options for {@link StatusBar}.\n *\n * @category Components\n */\nexport interface StatusBarOptions extends ContainerOptions {\n /** Initial transient message shown at the start of the row. */\n message?: string;\n /**\n * Fallback message restored when a timed {@link StatusBar.setMessage}\n * call expires. Defaults to the empty string.\n */\n defaultMessage?: string;\n}\n\n/**\n * User-overridable defaults forwarded to `super` via the options bag. The\n * cascade in [`Component`](/api/core/classes/Component)'s constructor\n * dispatches each present setter once with the final value. `message` and\n * `defaultMessage` are written pure to `_options` by `applyOptions` and\n * dispatched from the constructor body once the internal\n * [`Text`](/api/component/input/classes/Text) child exists.\n */\nconst _defaultStatusBarOptions: Partial<StatusBarOptions> = {\n insets: new Insets(0, 6, 0, 6),\n};\n\n/**\n * A thin horizontal strip mounted at the bottom of a window or panel that\n * surfaces a transient status message and small persistent indicators.\n *\n * `StatusBar` extends [`Container`](/api/core/classes/Container) and wraps a\n * single, non-stretching [`HBox`](/api/layout/classes/HBox) row: the message\n * [`Text`](/api/component/input/classes/Text), a flex spacer, then the\n * right-hand widgets. Every widget in the row is baseline-aligned to the\n * message text — a widget exposing a real baseline (`Text`, `Glyph`,\n * `IconText`, a labelled `Button`, `ProgressBar`, `ProgressSpinner`, or a\n * container laid out by a non-stretching `HBox`/`VBox`) lines its baseline up\n * with the message's; a baseline-less widget is centred in the message's text\n * line instead. Insert additional widgets via `addLeft` / `addRight` — for\n * example an [`IconText`](/api/component/display/classes/IconText) for a\n * connection light, a small [`ProgressBar`](/api/component/display/classes/ProgressBar),\n * or a [`ProgressSpinner`](/api/component/display/classes/ProgressSpinner).\n *\n * Widgets must be no taller than **21px** (`STATUS_BAR_HEIGHT` minus the 1px\n * top border) to fit without clipping. A stock `flat`+`compact` glyph-only\n * `Button` is 22px and does not fit — call `pinGlyphSize(14)` to bring it to\n * 20px before adding it.\n *\n * The strip is a single screen-reader live region (`role=\"status\"`,\n * `aria-live=\"polite\"`) so widget mutations announce politely without\n * per-widget opt-in.\n *\n * @example\n * ```typescript\n * const sb = new StatusBar({ defaultMessage: \"Ready\" });\n * sb.setMessage(\"Saved\", 2000);\n * container.addComponent(sb);\n * ```\n *\n * @category Components\n */\nclass StatusBar extends Container<StatusBarOptions> {\n\n private _message: string = \"\";\n private _defaultMessage: string = \"\";\n private _messageTimer: number | null = null;\n private _messageText!: Text;\n private _spacer!: Spacer; // the flex pivot: left widgets before it, right widgets after\n\n /**\n * Constructs a `StatusBar` with optional initial message and default\n * message.\n *\n * @param options - Optional configuration bag. See {@link StatusBarOptions}\n * for caller-overridable fields.\n */\n constructor(options?: StatusBarOptions) {\n super(options, _defaultStatusBarOptions);\n\n const row = new HBox();\n row.setComponentSpacing(STATUS_BAR_SPACING); // 4 — the zones' former internal spacing\n this.setLayoutManager(row); // stretching stays at its default (false) — that's what we want\n\n this.setBackgroundColor(\"var(--ts-ui-statusbar-bg, rgb(245, 245, 245))\");\n this.setForegroundColor(\"var(--ts-ui-statusbar-color, rgb(60, 60, 60))\");\n this.setBorder({ borderTop: `${STATUS_BAR_BORDER_TOP_WIDTH}px solid var(--ts-ui-statusbar-border, rgb(220, 220, 220))` });\n this.setMinSize(0, STATUS_BAR_HEIGHT);\n this.setMaxSize(Number.MAX_SAFE_INTEGER, STATUS_BAR_HEIGHT);\n\n this._messageText = new Text(\"\");\n // The bar's row anchor, not cosmetic padding: a 21px line box (the strip\n // height minus its top border) gives this Text a 21px preferred height and\n // a baseline of 16 rather than 16px/13. That deep baseline becomes the\n // row's rowAscent — what every other widget aligns to — and the 21px line\n // box is what makes the row exactly fill the band, so a baseline-less\n // widget is centred against 21px rather than a shorter text line. HBox has\n // no cross-axis centring (CENTER is inert in BoxLayout.crossPlacement), so\n // this anchor is the only thing centring the bar's content. Removing it\n // drops rowAscent to 13 and top-anchors the whole row.\n this._messageText.centerInHeight(STATUS_BAR_HEIGHT - STATUS_BAR_BORDER_TOP_WIDTH);\n\n this._spacer = Spacer.flex();\n\n this.addComponent(this._messageText);\n this.addComponent(this._spacer);\n\n this.getAria().setRole(\"status\");\n this.getAria().setLive(\"polite\");\n\n if (this._options.defaultMessage !== undefined) {\n this._defaultMessage = this._options.defaultMessage;\n }\n if (this._options.message !== undefined) {\n this.setMessage(this._options.message);\n } else if (this._defaultMessage !== \"\") {\n this._message = this._defaultMessage;\n this._messageText.setText(this._defaultMessage);\n }\n }\n\n /**\n * Applies a {@link StatusBarOptions} bag. Inherited Container fields cascade\n * through `super.applyOptions`; the `message` and `defaultMessage` fields\n * are written pure to `_options` here and dispatched from the constructor\n * body once the internal message [`Text`](/api/component/input/classes/Text)\n * exists.\n *\n * @param options - The options bag carrying the values to apply.\n */\n protected applyOptions(options: StatusBarOptions): this {\n super.applyOptions(options);\n\n if (options.message !== undefined) this._options.message = options.message;\n if (options.defaultMessage !== undefined) this._options.defaultMessage = options.defaultMessage;\n\n return this;\n }\n\n /**\n * Inserts a component before the flex pivot, after the message\n * [`Text`](/api/component/input/classes/Text) and any previously-added\n * left widgets. Baseline-aligned to the message text; widgets should be\n * no taller than 21px (`STATUS_BAR_HEIGHT` minus the 1px top border).\n *\n * @param component - The component to insert. Widgets should be small —\n * the bar height is fixed at {@link STATUS_BAR_HEIGHT}px.\n *\n * @returns This status bar, for method chaining.\n */\n addLeft(component: Component): this {\n this.insertComponent(component, this.getComponents().indexOf(this._spacer));\n\n return this;\n }\n\n /**\n * Appends a component after the flex pivot. Baseline-aligned to the\n * message text; widgets should be no taller than 21px\n * (`STATUS_BAR_HEIGHT` minus the 1px top border).\n *\n * @param component - The component to append. Widgets should be small —\n * the bar height is fixed at {@link STATUS_BAR_HEIGHT}px.\n *\n * @returns This status bar, for method chaining.\n */\n addRight(component: Component): this {\n this.addComponent(component);\n\n return this;\n }\n\n /**\n * Removes a component previously added via {@link addLeft}. No-op if\n * `component` is not a child of this bar.\n *\n * @param component - The component to remove.\n *\n * @returns This status bar, for method chaining.\n */\n removeLeft(component: Component): this {\n this.removeComponent(component);\n\n return this;\n }\n\n /**\n * Removes a component previously added via {@link addRight}. No-op if\n * `component` is not a child of this bar.\n *\n * @param component - The component to remove.\n *\n * @returns This status bar, for method chaining.\n */\n removeRight(component: Component): this {\n this.removeComponent(component);\n\n return this;\n }\n\n /**\n * Replaces the visible status message. When `timeoutMs` is supplied, the\n * default message is restored after that delay. A subsequent\n * `setMessage` call cancels any pending revert.\n *\n * @param text - The new message string.\n * @param timeoutMs - Optional. Milliseconds before the default message\n * is restored. Omit (or pass `0` / a negative value) for a persistent\n * message.\n *\n * @returns This status bar, for method chaining.\n */\n setMessage(text: string, timeoutMs?: number): this {\n if (this._messageTimer !== null) {\n clearTimeout(this._messageTimer);\n this._messageTimer = null;\n }\n\n this._message = text;\n this._messageText.setText(text);\n\n if (timeoutMs !== undefined && timeoutMs > 0) {\n this._messageTimer = setTimeout(() => {\n this._messageTimer = null;\n this._message = this._defaultMessage;\n this._messageText.setText(this._defaultMessage);\n }, timeoutMs);\n }\n\n return this;\n }\n\n /**\n * Returns the currently-visible message string.\n *\n * @returns The current message.\n */\n getMessage(): string {\n return this._message;\n }\n\n /**\n * Cancels any pending revert and reverts to the default message\n * immediately.\n *\n * @returns This status bar, for method chaining.\n */\n clearMessage(): this {\n return this.setMessage(this._defaultMessage);\n }\n\n /**\n * Sets the fallback message used when a timed {@link StatusBar.setMessage}\n * call expires. When no transient message is currently in flight (i.e.\n * no pending timer), the visible message is updated immediately so a\n * freshly configured `StatusBar` shows the new default.\n *\n * @param text - The new default message.\n *\n * @returns This status bar, for method chaining.\n */\n setDefaultMessage(text: string): this {\n this._defaultMessage = text;\n\n if (this._messageTimer === null) {\n this._message = text;\n this._messageText.setText(text);\n }\n\n return this;\n }\n\n /**\n * Returns the configured default message.\n *\n * @returns The current default message.\n */\n getDefaultMessage(): string {\n return this._defaultMessage;\n }\n\n /**\n * Clears any pending message-revert timer before the inherited\n * destructor detaches the element, preventing a stray `setTimeout`\n * callback from writing into a detached\n * [`Text`](/api/component/input/classes/Text).\n */\n protected destructor() {\n if (this._messageTimer !== null) {\n clearTimeout(this._messageTimer);\n this._messageTimer = null;\n }\n\n super.destructor();\n }\n}\n\nconst StatusBarCallable = callable(StatusBar);\ntype StatusBarCallable = StatusBar;\nexport {\n StatusBar as _StatusBar,\n StatusBarCallable as StatusBar,\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Container, ContainerOptions } from \"~/core/Container.js\";\nimport { Component } from \"~/core/Component.js\";\nimport { LayoutConstraints } from \"~/layout/LayoutConstraints.js\";\nimport { Tab, TabOptions } from \"~/layout/Tab.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * One entry in a {@link TabPanel}'s `tabs` options array.\n *\n * @category Components\n */\nexport interface TabEntryConfig {\n /** Label rendered in the tab button. */\n label: string;\n /** Component shown in the content area when this tab is selected. */\n component: Component;\n /** When `true`, a close button appears on the tab button. */\n closeable?: boolean;\n /** Optional registry glyph name shown leading the tab button's label. */\n glyph?: string;\n}\n\n/**\n * Construction-time options for {@link TabPanel}.\n *\n * @category Components\n */\nexport interface TabPanelOptions extends ContainerOptions {\n /** Optional initial set of tabs; each entry maps to one `addTab` call. */\n tabs?: TabEntryConfig[];\n /**\n * Optional callback fired when the user closes a tab via its close\n * button. Receives the closed tab's content component.\n */\n onTabClose?: (component: Component) => void;\n /**\n * Construction-time options for the wrapped {@link Tab} manager (strip\n * placement, width mode, scrolling, tools, compact, reorder, …). Passed\n * straight to the manager's constructor.\n */\n tabOptions?: TabOptions;\n}\n\n/**\n * A [`Container`](/api/core/classes/Container) subclass that owns an internal\n * [`Tab`](/api/layout/classes/Tab) layout manager and exposes a tab-typed\n * `addTab` / `addLazyTab` surface so consumers do not have to wire\n * `new Container({ layoutManager: new Tab() })` themselves. The bare\n * Container + Tab manager path still works unchanged; `TabPanel` is the\n * convenience entry point.\n *\n * Strip-level tuning (placement, width mode, scrolling, tools, …) is reached\n * through {@link getTab}, the typed accessor for the wrapped manager, rather\n * than a mirrored forwarder per setter.\n *\n * @example\n * ```typescript\n * import { TabPanel } from '@jimka/typescript-ui/component/container';\n *\n * const tabs = new TabPanel({\n * tabs: [\n * { label: 'Alpha', component: alphaPanel },\n * { label: 'Beta', component: betaPanel, closeable: true },\n * ],\n * onTabClose: c => console.log(\"Closed\", c.getId()),\n * });\n *\n * tabs.getTab().setSide(\"west\");\n * ```\n *\n * @category Components\n */\nclass TabPanel<TOptions extends TabPanelOptions = TabPanelOptions> extends Container<TOptions> {\n\n /**\n * Wires the panel to an internal `Tab` manager, dispatches the optional\n * `tabs` / `onTabClose` options to the relevant setters.\n *\n * @param options - Optional construction-time options applied to the panel.\n */\n constructor(options?: TOptions) {\n super(options);\n\n // Set the layout manager unconditionally — even if the caller passed\n // `layoutManager` via the options bag, `TabPanel`'s identity is the\n // `Tab` manager. Override-by-options would defeat the class. The strip\n // configuration rides in as the manager's own options bag.\n this.setLayoutManager(new Tab(options?.tabOptions));\n\n if (options?.tabs) {\n for (const entry of options.tabs) {\n this.addTab(entry.component, entry.label, { closeable: entry.closeable, glyph: entry.glyph });\n }\n }\n\n if (options?.onTabClose) {\n this.getTab().on(\"tabclose\", options.onTabClose);\n }\n }\n\n /**\n * Adds a tab to the panel's internal `Tab` manager.\n *\n * @param component - The content shown when this tab is selected.\n * @param label - The tab button's label.\n * @param options - Optional. Supports `{ closeable: true }` and a leading `glyph` name.\n *\n * @returns This panel, for method chaining.\n */\n addTab(component: Component, label: string, options?: { closeable?: boolean; glyph?: string }): this {\n const constraints = new LayoutConstraints();\n constraints.name = label;\n constraints.closeable = options?.closeable ?? false;\n constraints.glyph = options?.glyph ?? null;\n\n this.addComponent(component, constraints);\n\n return this;\n }\n\n /**\n * Registers a lazy tab. The factory runs on first activation; until then\n * the panel reserves the tab slot and shows a spinner placeholder when\n * the user selects the tab. Forwards to the wrapped `Tab.addLazyTab`.\n *\n * @param factory - Builds the content component on first activation.\n * @param label - The tab button's label.\n * @param options - Optional. Supports `{ closeable: true }` and a leading `glyph` name.\n *\n * @returns This panel, for method chaining.\n */\n addLazyTab(factory: () => Component, label: string, options?: { closeable?: boolean; glyph?: string }): this {\n const constraints = new LayoutConstraints();\n constraints.name = label;\n constraints.closeable = options?.closeable ?? false;\n constraints.glyph = options?.glyph ?? null;\n\n this.getTab().addLazyTab(factory, label, constraints);\n\n return this;\n }\n\n /**\n * Typed accessor for the internally-owned `Tab` manager. Use it to reach\n * strip-level configuration (placement, width mode, scrolling, tools,\n * events, …) and per-tab operations without casting `getLayoutManager()`.\n *\n * @returns The wrapped `Tab` instance.\n */\n getTab(): Tab {\n return this.getLayoutManager() as Tab;\n }\n}\n\nconst TabPanelCallable = callable(TabPanel);\ntype TabPanelCallable<TOptions extends TabPanelOptions = TabPanelOptions> = TabPanel<TOptions>;\nexport {\n TabPanel as _TabPanel,\n TabPanelCallable as TabPanel,\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Container, ContainerOptions } from \"~/core/Container.js\";\nimport { Component } from \"~/core/Component.js\";\nimport { Accordion, SectionToggleCallback } from \"~/layout/Accordion.js\";\nimport { AccordionConstraints } from \"~/layout/AccordionConstraints.js\";\nimport { LayoutConstraints } from \"~/layout/LayoutConstraints.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * One entry in an {@link AccordionPanel}'s `sections` options array.\n *\n * @category Components\n */\nexport interface AccordionSectionConfig {\n /** Label rendered in the section's header button. */\n label: string;\n /** Component shown inside the section's content area when expanded. */\n component: Component;\n /** Whether the section starts expanded. Defaults to `false` if omitted. */\n initiallyOpen?: boolean;\n /** Optional registry glyph name shown leading the section's header label. */\n glyph?: string;\n /** Optional per-section tool components shown in this section's header. */\n tools?: Component[];\n /**\n * Optional share of the accordion's leftover height this section fills when\n * the open sections underflow. `0`/omitted sits at preferred height; a\n * positive weight grows the section by its fraction of the total fill weight.\n * See {@link LayoutConstraints.weight}.\n */\n weight?: number;\n}\n\n/**\n * Construction-time options for {@link AccordionPanel}.\n *\n * @category Components\n */\nexport interface AccordionPanelOptions extends ContainerOptions {\n /** Optional initial sections; each entry maps to one `addSection` call. */\n sections?: AccordionSectionConfig[];\n /** Mirrors `Accordion.setSingleOpen` — only one section open at a time. */\n singleOpen?: boolean;\n /** Mirrors `Accordion.setResizable` — draggable gutters between open sections. */\n resizable?: boolean;\n /** Optional callback fired when a section opens or closes. */\n onSectionToggle?: SectionToggleCallback;\n}\n\n/**\n * A [`Container`](/api/core/classes/Container) subclass that owns an internal\n * [`Accordion`](/api/layout/classes/Accordion) layout manager and exposes a\n * section-typed `addSection` surface so consumers do not have to wire\n * `new Container({ layoutManager: new Accordion() })` themselves. The bare\n * Container + Accordion manager path still works unchanged; `AccordionPanel`\n * is the convenience entry point.\n *\n * Section operations (open/close, single-open mode, toggle events) are reached\n * through {@link getAccordion}, the typed accessor for the wrapped manager,\n * rather than a mirrored forwarder per method.\n *\n * @example\n * ```typescript\n * import { AccordionPanel } from '@jimka/typescript-ui/component/container';\n *\n * const acc = new AccordionPanel({\n * singleOpen: true,\n * sections: [\n * { label: 'Profile', component: profilePanel, initiallyOpen: true },\n * { label: 'Preferences', component: prefsPanel },\n * ],\n * });\n *\n * acc.getAccordion().openSection(1);\n * ```\n *\n * @category Components\n */\nclass AccordionPanel<TOptions extends AccordionPanelOptions = AccordionPanelOptions> extends Container<TOptions> {\n\n /**\n * Wires the panel to an internal `Accordion` manager, dispatches the\n * optional `sections` / `singleOpen` / `onSectionToggle` options to the\n * relevant setters.\n *\n * @param options - Optional construction-time options applied to the panel.\n */\n constructor(options?: TOptions) {\n super(options);\n\n // Set the layout manager unconditionally — even if the caller passed\n // `layoutManager` via the options bag, `AccordionPanel`'s identity is\n // the `Accordion` manager. Override-by-options would defeat the class.\n this.setLayoutManager(new Accordion());\n\n if (options?.singleOpen !== undefined) {\n this.getAccordion().setSingleOpen(options.singleOpen);\n }\n\n if (options?.resizable !== undefined) {\n this.getAccordion().setResizable(options.resizable);\n }\n\n if (options?.sections) {\n for (const section of options.sections) {\n this.addSection(section.component, section.label, section.initiallyOpen, section.glyph, section.tools, section.weight);\n }\n }\n\n if (options?.onSectionToggle) {\n this.getAccordion().on(\"sectiontoggle\", options.onSectionToggle);\n }\n }\n\n /**\n * Adds a section to the panel's internal `Accordion` manager.\n *\n * @param component - The content shown inside the section.\n * @param label - The section header's label.\n * @param initiallyOpen - Optional. Whether the section starts expanded; defaults to `false`.\n * @param glyph - Optional. Registry glyph name shown leading the header label.\n * @param tools - Optional. Per-section tool components for this header.\n * @param weight - Optional. Share of the accordion's leftover height this\n * section fills when the open sections underflow (see\n * {@link LayoutConstraints.weight}).\n *\n * @returns This panel, for method chaining.\n */\n addSection(component: Component, label: string, initiallyOpen?: boolean, glyph?: string, tools?: Component[], weight?: number): this {\n const constraints = new AccordionConstraints(label, initiallyOpen, glyph, tools);\n\n if (weight !== undefined) {\n constraints.weight = weight;\n }\n\n this.addComponent(component, constraints);\n\n return this;\n }\n\n /**\n * Typed accessor for the internally-owned `Accordion` manager. Use it to\n * reach section operations (open/close, single-open mode, toggle events)\n * without casting `getLayoutManager()`.\n *\n * @returns The wrapped `Accordion` instance.\n */\n getAccordion(): Accordion {\n return this.getLayoutManager() as Accordion;\n }\n}\n\nconst AccordionPanelCallable = callable(AccordionPanel);\ntype AccordionPanelCallable<TOptions extends AccordionPanelOptions = AccordionPanelOptions> = AccordionPanel<TOptions>;\nexport {\n AccordionPanel as _AccordionPanel,\n AccordionPanelCallable as AccordionPanel,\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"container.es.js","names":[],"sources":["../../../src/typescript/lib/component/container/Legend.ts","../../../src/typescript/lib/component/container/FieldSet.ts","../../../src/typescript/lib/component/container/LabeledGrid.ts","../../../src/typescript/lib/component/container/LabeledFieldSet.ts","../../../src/typescript/lib/component/container/StatusBar.ts","../../../src/typescript/lib/component/container/TabPanel.ts","../../../src/typescript/lib/component/container/AccordionPanel.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Text, TextOptions } from \"~/component/input/Text.js\"\nimport { Position } from \"~/primitive/Position.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport type { Handle } from \"~/core/DOM.js\";\n\n/**\n * Construction-time options for {@link Legend}.\n *\n * @category Components\n */\nexport interface LegendOptions extends TextOptions {\n}\n\n/**\n * A legend component backed by a `<legend>` element.\n *\n * Uses static CSS positioning so the browser can render the title text inside a fieldset border.\n *\n * @category Components\n */\nclass Legend extends Text<LegendOptions> {\n\n /** Left inset (px) so the title clears the fieldset's left border corner. */\n private static readonly LEFT_MARGIN = 10;\n\n constructor(options?: LegendOptions) {\n // `tag` is structural — the element type is by definition `<legend>`.\n super(undefined, {\n ...(options ?? {}),\n tag: \"legend\",\n });\n\n // `<legend>` only renders inside its fieldset's notch when positioned\n // statically — the framework's \"every component is absolute\" rule\n // would float the legend out of the border. Documented HTML-semantics\n // exception alongside `FIXED` floating overlays. See\n // ARCHITECTURE.md §Positioning.\n this.setPosition(Position.STATIC);\n }\n\n /**\n * Applies base styles, then re-asserts a left margin so the legend clears\n * the fieldset's left border corner instead of hugging it. `Component`'s\n * `applyStyle` zeroes the `margin` shorthand on every component, which\n * strips the browser's default `<legend>` inset; this longhand write runs\n * after `super` and overrides only the left side.\n *\n * @param element - The element handle to apply styles to.\n */\n applyStyle(element: Handle): this {\n super.applyStyle(element);\n\n this.setElementCSSRule(\"marginLeft\", `${Legend.LEFT_MARGIN}px`);\n\n return this;\n }\n}\n\nconst LegendCallable = callable(Legend);\ntype LegendCallable = Legend;\nexport {\n Legend as _Legend,\n LegendCallable as Legend\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component, ComponentOptions } from \"~/core/Component.js\";\nimport { DOM } from \"~/core/DOM.js\";\nimport { Legend } from \"~/component/container/Legend.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { Size } from \"~/primitive/Size.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Construction-time options for {@link FieldSet}.\n *\n * @category Components\n */\nexport interface FieldSetOptions extends ComponentOptions {\n legend?: string;\n}\n\n/**\n * Subclass defaults layered into `Component._defaultOptions` via the second\n * super arg. Any field the caller omits falls back to one of these values via\n * the getters / `applyStyle`, which consult `_defaultOptions` directly — the\n * default is never dispatched into `_options`.\n */\nconst _defaultFieldSetOptions: Partial<FieldSetOptions> = {\n tag: \"fieldset\",\n border: \"var(--ts-ui-input-border)\",\n borderRadius: \"var(--ts-ui-border-radius, 4px)\",\n // Intrinsic chrome carried as insets, not CSS padding. Top (5) is the gap\n // above content; the legend's own reserved height is added separately in\n // getPerimeterSize so it counts toward the box height without also pushing\n // the child origin (a <fieldset> already offsets its absolutely positioned\n // children below the legend). Sides/bottom (8) are the inner gutter.\n insets: new Insets(5, 8, 8, 8),\n preferredSize: { width: 200, height: 200 },\n minSize: { width: 100, height: 100 },\n};\n\n/**\n * A fieldset component with an embedded legend title.\n *\n * Renders a `<fieldset>` element and prepends a Legend child for the group title.\n *\n * @category Components\n */\nclass FieldSet extends Component {\n\n /** Legend clearance (px) used before the legend element can be measured. */\n private static readonly LEGEND_CLEARANCE_FALLBACK = 16;\n\n private _legend: Legend = new Legend();\n\n /** Cached measured legend height; null until a positive measurement lands. */\n private _legendClearance: number | null = null;\n\n constructor(title: string = \"\", options?: FieldSetOptions, subclassDefaults?: Partial<FieldSetOptions>) {\n super(options, { ..._defaultFieldSetOptions, ...(subclassDefaults ?? {}) });\n\n this._legend.setText(title);\n this._legend.setDisplayed(title !== \"\");\n }\n\n /**\n * Applies a {@link FieldSetOptions} bag, dispatching the legend title text\n * after inherited Component fields.\n *\n * @param options - The options bag carrying the values to apply.\n */\n protected applyOptions(options: FieldSetOptions): this {\n super.applyOptions(options);\n\n if (options.legend !== undefined) {\n this.setTitle(options.legend);\n }\n\n return this;\n }\n\n /**\n * Returns the fieldset legend title text.\n *\n * @returns The current legend text string.\n */\n getTitle() {\n return this._legend.getText();\n }\n\n /**\n * Sets the fieldset legend title text.\n *\n * @param title - The text to display in the legend.\n */\n setTitle(title: string) : this {\n this._legend.setText(title);\n this._legend.setDisplayed(title !== \"\");\n this.clampLegendWidth();\n\n return this;\n }\n\n /**\n * Clamps the legend's `max-width` to the fieldset's current inner width so\n * a long title ellipsises inside the border notch instead of spilling out.\n * The legend already carries `overflow:hidden; text-overflow:ellipsis;\n * white-space:nowrap` (from `Text`'s `truncate:true`), so a `max-width` is\n * the only missing piece for the ellipsis to engage.\n *\n * @returns This component, for method chaining.\n *\n * @remarks No-op until the first layout pass commits a width — `getWidth()`\n * returns 0 on a detached / unsized fieldset. `doLayout()` re-runs the\n * clamp once a width exists.\n */\n private clampLegendWidth(): this {\n if (this.getTitle() === \"\") {\n return this;\n }\n\n const width = this.getWidth();\n if (width <= 0) {\n return this;\n }\n\n const perim = this.getPerimeterSize();\n const chromeW = perim.left + perim.right;\n const innerW = Math.max(0, width - chromeW);\n\n this._legend.setMaxSize(innerW, Number.MAX_VALUE);\n\n return this;\n }\n\n /**\n * Returns the minimum size, augmented to include the legend's measured\n * width so the legend never spills out of the border notch. The legend is\n * rendered statically by the browser (it's not in the framework layout\n * tree), so `super.getMinSize()` from the layout manager doesn't see it.\n *\n * @returns The minimum `{width, height}`, ensuring the legend's text fits.\n */\n getMinSize(): Size | null {\n if (this.getTitle() === \"\") {\n return super.getMinSize();\n }\n\n const baseMin = super.getMinSize();\n const legendMin = this._legend.getMinSize();\n if (!legendMin) {\n return baseMin;\n }\n\n const perim = this.getPerimeterSize();\n const chromeW = perim.left + perim.right;\n\n const fieldsetW = legendMin.width + chromeW;\n if (!baseMin) {\n return { width: fieldsetW, height: 0 };\n }\n\n return {\n width: Math.max(baseMin.width, fieldsetW),\n height: baseMin.height,\n };\n }\n\n /**\n * Augments the top perimeter with the legend's reserved vertical space.\n *\n * A `<fieldset>` reserves room for its `<legend>` at the top of its content\n * box automatically: absolutely positioned children begin below the legend\n * regardless of the framework's insets, and the browser supplies that\n * offset itself. {@link getInnerSize} must account for the same space or\n * the bottom row overflows the border, so this adds the legend's height to\n * the `top` perimeter. The child origin is deliberately left untouched —\n * the content-inset origin derives from insets alone, and the browser\n * already provides the matching downward offset, so adding it to the\n * origin too would double-count it.\n *\n * The legend is browser-rendered chrome, not a framework-layout child, so\n * its height is read from the element and cached. The measurement slightly\n * over-reserves when the legend is raised into the border notch, which errs\n * toward extra bottom space rather than clipping. Before the element can be\n * measured a constant fallback is used and not cached, so the real height\n * supersedes it on the first connected pass.\n *\n * @returns The base perimeter with the legend clearance added to `top`.\n */\n getPerimeterSize() {\n const perim = super.getPerimeterSize();\n\n perim.top += this.legendClearance();\n\n return perim;\n }\n\n /**\n * Returns the legend's reserved top clearance in pixels: the measured\n * legend height once it is connected and laid out (cached after the first\n * positive measurement), or {@link LEGEND_CLEARANCE_FALLBACK} until then.\n *\n * @returns The legend clearance in pixels.\n */\n private legendClearance(): number {\n if (this.getTitle() === \"\") {\n return 0;\n }\n\n if (this._legendClearance != null) {\n return this._legendClearance;\n }\n\n // A modelled (no-browser) source has no native `<legend>` box to\n // measure, so short-circuit to the fallback rather than reading\n // `offsetHeight`, which is 0 or unavailable offline.\n if (DOM.source.isModelled()) {\n return FieldSet.LEGEND_CLEARANCE_FALLBACK;\n }\n\n const element = this._legend.getElement();\n\n if (element && DOM.source.isConnected(element)) {\n const offsetHeight = DOM.source.getOffsetSize(element).offsetHeight;\n\n if (offsetHeight > 0) {\n this._legendClearance = offsetHeight;\n\n return this._legendClearance;\n }\n }\n\n return FieldSet.LEGEND_CLEARANCE_FALLBACK;\n }\n\n /**\n * Lays out the fieldset, then re-clamps the legend to the freshly committed\n * inner width so the title ellipsis tracks fieldset resizes. The clamp runs\n * post-layout because `getWidth()` only reports a real width once the layout\n * pass has committed one.\n *\n * @returns This component, for method chaining.\n */\n doLayout(): this {\n super.doLayout();\n this.clampLegendWidth();\n\n return this;\n }\n\n /**\n * Renders the fieldset element and appends the legend as its first child.\n *\n * @returns The created HTMLFieldSetElement with the legend prepended.\n */\n render() {\n let element = super.render();\n\n DOM.sink.appendChild(element, this._legend.getElement(true)!);\n\n return element;\n }\n}\n\nconst FieldSetCallable = callable(FieldSet);\ntype FieldSetCallable = FieldSet;\nexport {\n FieldSet as _FieldSet,\n FieldSetCallable as FieldSet\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { Container, ContainerOptions } from \"~/core/Container.js\";\nimport { Grid } from \"~/layout/Grid.js\";\nimport { GridConstraints } from \"~/layout/GridConstraints.js\";\nimport { GridTrack } from \"~/layout/GridTrack.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Default inter-cell spacing (px) for the internal form grid. Mirrors the\n * hand-rolled Binding demo's `spacing: 8` so a {@link LabeledGrid} matches the\n * reference labelled-form look without the caller restating it.\n */\nconst FIELD_SPACING_DEFAULT = 8;\n\n/**\n * A title/field pair: a label and the input it labels.\n *\n * @category Components\n */\nexport interface LabeledFieldDescriptor {\n\n /** Label text rendered as a baseline-aligned `Text`. */\n title: string;\n\n /** The input/component placed beside the label. */\n component: Component;\n}\n\n/**\n * One row of a {@link LabeledGrid}: either an array of pairs (one per logical\n * column, left-to-right; a short array leaves trailing columns empty), or a\n * single component that spans every column (status lines, button bars).\n *\n * @category Components\n */\nexport type LabeledRowDescriptor =\n | LabeledFieldDescriptor[]\n | { component: Component; fullWidth: true };\n\n/**\n * Construction-time options for {@link LabeledGrid}.\n *\n * @category Components\n */\nexport interface LabeledGridOptions extends ContainerOptions {\n\n /** Logical title/field columns laid side by side. Default `1`. */\n columns?: number;\n\n /** Inter-cell spacing in px for the grid. Default mirrors the demo's `8`. */\n fieldSpacing?: number;\n\n /** Declarative rows, applied in order at construction via the same path as `addRow`. */\n rows?: LabeledRowDescriptor[];\n}\n\n/**\n * A chrome-less baseline-aligned title/field grid, rendered in a plain `<div>`\n * with no border and no legend. {@link LabeledFieldSet} composes one of these\n * inside its `<fieldset>` chrome; use `LabeledGrid` directly wherever the\n * baseline-aligned form layout is wanted without the fieldset border/legend.\n *\n * The body is a single [`Grid`](/api/layout/classes/Grid) in\n * `baselineAlign` mode with `2 × columns` grid-columns: each logical column is\n * a content-sized title track followed by a weight-sized input track, so titles\n * hug their text while inputs share a common right edge per column. Pairs flow\n * left-to-right and wrap to a new row when the current one fills; a full-width\n * row spans every column via [`GridConstraints`](/api/layout/classes/GridConstraints)\n * `colSpan`.\n *\n * @category Components\n */\nclass LabeledGrid extends Container<LabeledGridOptions> {\n\n /** Logical title/field column count (the grid has `2 ×` this many grid-columns). */\n private _columns: number;\n\n /** The internal baseline grid; its `rows`/`rowTracks` grow as rows are added. */\n private _grid: Grid;\n\n /** Next free grid-column on the current flow row (0-based, in grid-columns). */\n private _flowCol: number = 0;\n\n /** Running grid-row count, pushed into the grid's `rows` + `rowTracks`. */\n private _rowCount: number = 0;\n\n /**\n * Builds the grid, installs the internal baseline grid, and replays any\n * declarative `rows`.\n *\n * @param options - Grid structure: `columns`, `fieldSpacing`, `rows`.\n */\n constructor(options?: LabeledGridOptions) {\n super(options);\n\n this._columns = options?.columns ?? 1;\n\n this._grid = new Grid({\n baselineAlign: true,\n columns: 2 * this._columns,\n spacing: options?.fieldSpacing ?? FIELD_SPACING_DEFAULT,\n columnTracks: this.buildColumnTracks(),\n rows: 0,\n rowTracks: [],\n });\n\n this.setLayoutManager(this._grid);\n\n if (options?.rows) {\n this.applyRows(options.rows);\n }\n }\n\n /**\n * Appends one title/field pair into the next free logical column, flowing to\n * a new row when the current one fills.\n *\n * @param title - Label text placed in the content (title) column.\n * @param component - The input placed in the weight (field) column.\n * @returns This component, for method chaining.\n */\n addField(title: string, component: Component): this {\n this.openRow();\n\n this.addComponent(new Text(title));\n this.addComponent(component);\n\n this._flowCol += 2;\n\n if (this._flowCol >= 2 * this._columns) {\n this._flowCol = 0;\n }\n\n return this;\n }\n\n /**\n * Appends a full row of pairs (one per logical column; trailing columns are\n * left empty for a short array).\n *\n * @param fields - The pairs to place across the row, left-to-right.\n * @returns This component, for method chaining.\n */\n addRow(fields: LabeledFieldDescriptor[]): this {\n this.finishRow();\n\n for (const field of fields) {\n this.addField(field.title, field.component);\n }\n\n this.finishRow();\n\n return this;\n }\n\n /**\n * Appends a component spanning every column on its own row.\n *\n * @param component - The component to span the full grid width.\n * @returns This component, for method chaining.\n */\n addFullWidthRow(component: Component): this {\n this.finishRow();\n this.openRow();\n\n const constraints = new GridConstraints();\n constraints.colSpan = 2 * this._columns;\n\n this.addComponent(component, constraints);\n\n return this;\n }\n\n /**\n * Returns the configured logical column count.\n *\n * @returns The number of side-by-side title/field columns.\n */\n getColumns(): number {\n return this._columns;\n }\n\n /**\n * Builds the `2 × columns` grid-column tracks: for each logical column a\n * content-sized title track (hugs its text) followed by a weight-sized input\n * track (takes the slack so inputs share a right edge).\n *\n * @returns The column tracks for the internal grid.\n */\n private buildColumnTracks(): GridTrack[] {\n const tracks: GridTrack[] = [];\n\n for (let column = 0; column < this._columns; column++) {\n tracks.push({ mode: \"content\" });\n tracks.push({ mode: \"weight\", value: 1 });\n }\n\n return tracks;\n }\n\n /**\n * Replays the declarative `rows` option through the same add path as the\n * imperative API, so construction and runtime calls share one code path.\n *\n * @param rows - The declarative row descriptors to apply, in order.\n */\n private applyRows(rows: LabeledRowDescriptor[]): void {\n for (const row of rows) {\n if (Array.isArray(row)) {\n this.addRow(row);\n } else {\n this.addFullWidthRow(row.component);\n }\n }\n }\n\n /**\n * Opens a new flow row when the first cell of one is about to be added,\n * bumping the running row count and growing the grid so its baseline\n * auto-flow does not stop short of the new row.\n */\n private openRow(): void {\n if (this._flowCol === 0) {\n this._rowCount++;\n this.growRows();\n }\n }\n\n /**\n * Pads any partially-filled flow row with empty spacer cells so the next row\n * starts at grid-column 0. The spacers carry no preferred size, contributing\n * 0 to the grid's content measurement.\n */\n private finishRow(): void {\n if (this._flowCol === 0) {\n return;\n }\n\n const remaining = 2 * this._columns - this._flowCol;\n\n for (let cell = 0; cell < remaining; cell++) {\n this.addComponent(new Component());\n }\n\n this._flowCol = 0;\n }\n\n /**\n * Re-sizes the grid's row count and row tracks to the running row total.\n * Every row track is `content`-sized so inputs keep their natural height.\n */\n private growRows(): void {\n this._grid.setRows(this._rowCount);\n this._grid.setRowTracks(Array.from({ length: this._rowCount }, () => ({ mode: \"content\" as const })));\n }\n}\n\nconst LabeledGridCallable = callable(LabeledGrid);\ntype LabeledGridCallable = LabeledGrid;\nexport {\n LabeledGrid as _LabeledGrid,\n LabeledGridCallable as LabeledGrid\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { Fit } from \"~/layout/Fit.js\";\nimport { LabeledGrid, LabeledGridOptions, LabeledFieldDescriptor } from \"~/component/container/LabeledGrid.js\";\nimport { _FieldSet, FieldSetOptions } from \"~/component/container/FieldSet.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * Construction-time options for {@link LabeledFieldSet}.\n *\n * @category Components\n */\nexport interface LabeledFieldSetOptions extends FieldSetOptions, LabeledGridOptions {}\n\n/**\n * Subclass defaults layered under the caller's options. Clears the base\n * FieldSet's fixed 200x200 preferred size and fixed 100x100 min size: a\n * labeled fieldset is exactly as large as its computed grid content, so the\n * fixed values would otherwise pad a short form with dead space (preferred)\n * or force a min floor no tiny form needs (min). With both cleared,\n * `Component.getPreferredSize` / `getMinSize` fall through to the internal\n * grid (forwarded through the `Fit` layout), whose reports already add the\n * legend clearance and insets.\n */\nconst _defaultLabeledFieldSetOptions: Partial<LabeledFieldSetOptions> = {\n preferredSize: undefined,\n minSize: undefined,\n};\n\n/**\n * A {@link FieldSet} whose content is a baseline-aligned form of title/field\n * pairs, generalising the hand-rolled labelled-form pattern from the Binding\n * demo into a reusable container.\n *\n * `LabeledFieldSet` composes a {@link LabeledGrid} inside its `<fieldset>`\n * chrome via a `Fit` layout: `LabeledFieldSet` = a `LabeledGrid` inside a\n * `FieldSet`. Use a bare `LabeledGrid` directly when the baseline-aligned\n * form layout is wanted without the fieldset border/legend.\n *\n * @category Components\n */\nclass LabeledFieldSet extends _FieldSet {\n\n /** The internal chrome-less grid this fieldset composes. */\n private _labeledGrid: LabeledGrid;\n\n /**\n * Builds the fieldset chrome and installs an internal {@link LabeledGrid}\n * via a `Fit` layout.\n *\n * @param title - The legend title (forwarded to {@link FieldSet}).\n * @param options - Form structure: `columns`, `fieldSpacing`, `rows`.\n * @param subclassDefaults - Defaults a subclass layers under the caller's options.\n */\n constructor(title: string = \"\", options?: LabeledFieldSetOptions, subclassDefaults?: Partial<LabeledFieldSetOptions>) {\n super(title, options, { ..._defaultLabeledFieldSetOptions, ...(subclassDefaults ?? {}) });\n\n this._labeledGrid = new LabeledGrid({\n columns: options?.columns,\n fieldSpacing: options?.fieldSpacing,\n rows: options?.rows,\n });\n\n this.setLayoutManager(new Fit());\n this.addComponent(this._labeledGrid);\n }\n\n /**\n * Appends one title/field pair into the next free logical column, flowing to\n * a new row when the current one fills.\n *\n * @param title - Label text placed in the content (title) column.\n * @param component - The input placed in the weight (field) column.\n * @returns This component, for method chaining.\n */\n addField(title: string, component: Component): this {\n this._labeledGrid.addField(title, component);\n\n return this;\n }\n\n /**\n * Appends a full row of pairs (one per logical column; trailing columns are\n * left empty for a short array).\n *\n * @param fields - The pairs to place across the row, left-to-right.\n * @returns This component, for method chaining.\n */\n addRow(fields: LabeledFieldDescriptor[]): this {\n this._labeledGrid.addRow(fields);\n\n return this;\n }\n\n /**\n * Appends a component spanning every column on its own row.\n *\n * @param component - The component to span the full form width.\n * @returns This component, for method chaining.\n */\n addFullWidthRow(component: Component): this {\n this._labeledGrid.addFullWidthRow(component);\n\n return this;\n }\n\n /**\n * Returns the configured logical column count.\n *\n * @returns The number of side-by-side title/field columns.\n */\n getColumns(): number {\n return this._labeledGrid.getColumns();\n }\n\n /**\n * Typed accessor for the internally-composed {@link LabeledGrid}. Use it\n * to inspect the grid's cells directly (e.g. `getGrid().getComponents()`).\n *\n * @returns The wrapped `LabeledGrid` instance.\n */\n getGrid(): LabeledGrid {\n return this._labeledGrid;\n }\n}\n\nconst LabeledFieldSetCallable = callable(LabeledFieldSet);\ntype LabeledFieldSetCallable = LabeledFieldSet;\nexport {\n LabeledFieldSet as _LabeledFieldSet,\n LabeledFieldSetCallable as LabeledFieldSet\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Component } from \"~/core/Component.js\";\nimport { Container, ContainerOptions } from \"~/core/Container.js\";\nimport { Text } from \"~/component/input/Text.js\";\nimport { HBox } from \"~/layout/HBox.js\";\nimport { Insets } from \"~/primitive/Insets.js\";\nimport { callable } from \"~/core/Callable.js\";\nimport { Spacer } from \"./Spacer\";\n\n/**\n * Fixed strip height for every `StatusBar` instance. Kept in lockstep with the\n * `--ts-ui-statusbar-height` token in [Theme.ts](/api/core/interfaces/Theme);\n * changing one without the other lets the bar grow taller than its chrome\n * background suggests.\n */\nexport const STATUS_BAR_HEIGHT: number = 22;\n\n/**\n * Pixel width of the bar's top separator border. Hardcoded rather than\n * read from the theme because the value is part of the bar's box-model\n * arithmetic — it determines the inner content height\n * (`STATUS_BAR_HEIGHT - STATUS_BAR_BORDER_TOP_WIDTH`) that the message\n * text line-height must match for vertical centring. A theme that wanted\n * a thicker rule would have to bump `STATUS_BAR_HEIGHT` in lockstep.\n */\nconst STATUS_BAR_BORDER_TOP_WIDTH: number = 1;\n\n/**\n * Gap between adjacent status-bar widgets. Carries over the spacing the\n * former per-zone `HBox`es used; the outer row previously used 0 because\n * the zones butted against the flex spacer, which absorbs any gap anyway.\n */\nconst STATUS_BAR_SPACING: number = 4;\n\n/**\n * Construction-time options for {@link StatusBar}.\n *\n * @category Components\n */\nexport interface StatusBarOptions extends ContainerOptions {\n /** Initial transient message shown at the start of the row. */\n message?: string;\n /**\n * Fallback message restored when a timed {@link StatusBar.setMessage}\n * call expires. Defaults to the empty string.\n */\n defaultMessage?: string;\n}\n\n/**\n * User-overridable defaults forwarded to `super` via the options bag. The\n * cascade in [`Component`](/api/core/classes/Component)'s constructor\n * dispatches each present setter once with the final value. `message` and\n * `defaultMessage` are written pure to `_options` by `applyOptions` and\n * dispatched from the constructor body once the internal\n * [`Text`](/api/component/input/classes/Text) child exists.\n */\nconst _defaultStatusBarOptions: Partial<StatusBarOptions> = {\n insets: new Insets(0, 6, 0, 6),\n};\n\n/**\n * A thin horizontal strip mounted at the bottom of a window or panel that\n * surfaces a transient status message and small persistent indicators.\n *\n * `StatusBar` extends [`Container`](/api/core/classes/Container) and wraps a\n * single, non-stretching [`HBox`](/api/layout/classes/HBox) row: the message\n * [`Text`](/api/component/input/classes/Text), a flex spacer, then the\n * right-hand widgets. Every widget in the row is baseline-aligned to the\n * message text — a widget exposing a real baseline (`Text`, `Glyph`,\n * `IconText`, a labelled `Button`, `ProgressBar`, `ProgressSpinner`, or a\n * container laid out by a non-stretching `HBox`/`VBox`) lines its baseline up\n * with the message's; a baseline-less widget is centred in the message's text\n * line instead. Insert additional widgets via `addLeft` / `addRight` — for\n * example an [`IconText`](/api/component/display/classes/IconText) for a\n * connection light, a small [`ProgressBar`](/api/component/display/classes/ProgressBar),\n * or a [`ProgressSpinner`](/api/component/display/classes/ProgressSpinner).\n *\n * Widgets must be no taller than **21px** (`STATUS_BAR_HEIGHT` minus the 1px\n * top border) to fit without clipping. A stock `flat`+`compact` glyph-only\n * `Button` is 22px and does not fit — call `pinGlyphSize(14)` to bring it to\n * 20px before adding it.\n *\n * The strip is a single screen-reader live region (`role=\"status\"`,\n * `aria-live=\"polite\"`) so widget mutations announce politely without\n * per-widget opt-in.\n *\n * @example\n * ```typescript\n * const sb = new StatusBar({ defaultMessage: \"Ready\" });\n * sb.setMessage(\"Saved\", 2000);\n * container.addComponent(sb);\n * ```\n *\n * @category Components\n */\nclass StatusBar extends Container<StatusBarOptions> {\n\n private _message: string = \"\";\n private _defaultMessage: string = \"\";\n private _messageTimer: number | null = null;\n private _messageText!: Text;\n private _spacer!: Spacer; // the flex pivot: left widgets before it, right widgets after\n\n /**\n * Constructs a `StatusBar` with optional initial message and default\n * message.\n *\n * @param options - Optional configuration bag. See {@link StatusBarOptions}\n * for caller-overridable fields.\n */\n constructor(options?: StatusBarOptions) {\n super(options, _defaultStatusBarOptions);\n\n const row = new HBox();\n row.setComponentSpacing(STATUS_BAR_SPACING); // 4 — the zones' former internal spacing\n this.setLayoutManager(row); // stretching stays at its default (false) — that's what we want\n\n this.setBackgroundColor(\"var(--ts-ui-statusbar-bg, rgb(245, 245, 245))\");\n this.setForegroundColor(\"var(--ts-ui-statusbar-color, rgb(60, 60, 60))\");\n this.setBorder({ borderTop: `${STATUS_BAR_BORDER_TOP_WIDTH}px solid var(--ts-ui-statusbar-border, rgb(220, 220, 220))` });\n this.setMinSize(0, STATUS_BAR_HEIGHT);\n this.setMaxSize(Number.MAX_SAFE_INTEGER, STATUS_BAR_HEIGHT);\n\n this._messageText = new Text(\"\");\n // The bar's row anchor, not cosmetic padding: a 21px line box (the strip\n // height minus its top border) gives this Text a 21px preferred height and\n // a baseline of 16 rather than 16px/13. That deep baseline becomes the\n // row's rowAscent — what every other widget aligns to — and the 21px line\n // box is what makes the row exactly fill the band, so a baseline-less\n // widget is centred against 21px rather than a shorter text line. HBox has\n // no cross-axis centring (CENTER is inert in BoxLayout.crossPlacement), so\n // this anchor is the only thing centring the bar's content. Removing it\n // drops rowAscent to 13 and top-anchors the whole row.\n this._messageText.centerInHeight(STATUS_BAR_HEIGHT - STATUS_BAR_BORDER_TOP_WIDTH);\n\n this._spacer = Spacer.flex();\n\n this.addComponent(this._messageText);\n this.addComponent(this._spacer);\n\n this.getAria().setRole(\"status\");\n this.getAria().setLive(\"polite\");\n\n if (this._options.defaultMessage !== undefined) {\n this._defaultMessage = this._options.defaultMessage;\n }\n if (this._options.message !== undefined) {\n this.setMessage(this._options.message);\n } else if (this._defaultMessage !== \"\") {\n this._message = this._defaultMessage;\n this._messageText.setText(this._defaultMessage);\n }\n }\n\n /**\n * Applies a {@link StatusBarOptions} bag. Inherited Container fields cascade\n * through `super.applyOptions`; the `message` and `defaultMessage` fields\n * are written pure to `_options` here and dispatched from the constructor\n * body once the internal message [`Text`](/api/component/input/classes/Text)\n * exists.\n *\n * @param options - The options bag carrying the values to apply.\n */\n protected applyOptions(options: StatusBarOptions): this {\n super.applyOptions(options);\n\n if (options.message !== undefined) this._options.message = options.message;\n if (options.defaultMessage !== undefined) this._options.defaultMessage = options.defaultMessage;\n\n return this;\n }\n\n /**\n * Inserts a component before the flex pivot, after the message\n * [`Text`](/api/component/input/classes/Text) and any previously-added\n * left widgets. Baseline-aligned to the message text; widgets should be\n * no taller than 21px (`STATUS_BAR_HEIGHT` minus the 1px top border).\n *\n * @param component - The component to insert. Widgets should be small —\n * the bar height is fixed at {@link STATUS_BAR_HEIGHT}px.\n *\n * @returns This status bar, for method chaining.\n */\n addLeft(component: Component): this {\n this.insertComponent(component, this.getComponents().indexOf(this._spacer));\n\n return this;\n }\n\n /**\n * Appends a component after the flex pivot. Baseline-aligned to the\n * message text; widgets should be no taller than 21px\n * (`STATUS_BAR_HEIGHT` minus the 1px top border).\n *\n * @param component - The component to append. Widgets should be small —\n * the bar height is fixed at {@link STATUS_BAR_HEIGHT}px.\n *\n * @returns This status bar, for method chaining.\n */\n addRight(component: Component): this {\n this.addComponent(component);\n\n return this;\n }\n\n /**\n * Removes a component previously added via {@link addLeft}. No-op if\n * `component` is not a child of this bar.\n *\n * @param component - The component to remove.\n *\n * @returns This status bar, for method chaining.\n */\n removeLeft(component: Component): this {\n this.removeComponent(component);\n\n return this;\n }\n\n /**\n * Removes a component previously added via {@link addRight}. No-op if\n * `component` is not a child of this bar.\n *\n * @param component - The component to remove.\n *\n * @returns This status bar, for method chaining.\n */\n removeRight(component: Component): this {\n this.removeComponent(component);\n\n return this;\n }\n\n /**\n * Replaces the visible status message. When `timeoutMs` is supplied, the\n * default message is restored after that delay. A subsequent\n * `setMessage` call cancels any pending revert.\n *\n * @param text - The new message string.\n * @param timeoutMs - Optional. Milliseconds before the default message\n * is restored. Omit (or pass `0` / a negative value) for a persistent\n * message.\n *\n * @returns This status bar, for method chaining.\n */\n setMessage(text: string, timeoutMs?: number): this {\n if (this._messageTimer !== null) {\n clearTimeout(this._messageTimer);\n this._messageTimer = null;\n }\n\n this._message = text;\n this._messageText.setText(text);\n\n if (timeoutMs !== undefined && timeoutMs > 0) {\n this._messageTimer = setTimeout(() => {\n this._messageTimer = null;\n this._message = this._defaultMessage;\n this._messageText.setText(this._defaultMessage);\n }, timeoutMs);\n }\n\n return this;\n }\n\n /**\n * Returns the currently-visible message string.\n *\n * @returns The current message.\n */\n getMessage(): string {\n return this._message;\n }\n\n /**\n * Cancels any pending revert and reverts to the default message\n * immediately.\n *\n * @returns This status bar, for method chaining.\n */\n clearMessage(): this {\n return this.setMessage(this._defaultMessage);\n }\n\n /**\n * Sets the fallback message used when a timed {@link StatusBar.setMessage}\n * call expires. When no transient message is currently in flight (i.e.\n * no pending timer), the visible message is updated immediately so a\n * freshly configured `StatusBar` shows the new default.\n *\n * @param text - The new default message.\n *\n * @returns This status bar, for method chaining.\n */\n setDefaultMessage(text: string): this {\n this._defaultMessage = text;\n\n if (this._messageTimer === null) {\n this._message = text;\n this._messageText.setText(text);\n }\n\n return this;\n }\n\n /**\n * Returns the configured default message.\n *\n * @returns The current default message.\n */\n getDefaultMessage(): string {\n return this._defaultMessage;\n }\n\n /**\n * Clears any pending message-revert timer before the inherited\n * destructor detaches the element, preventing a stray `setTimeout`\n * callback from writing into a detached\n * [`Text`](/api/component/input/classes/Text).\n */\n protected destructor() {\n if (this._messageTimer !== null) {\n clearTimeout(this._messageTimer);\n this._messageTimer = null;\n }\n\n super.destructor();\n }\n}\n\nconst StatusBarCallable = callable(StatusBar);\ntype StatusBarCallable = StatusBar;\nexport {\n StatusBar as _StatusBar,\n StatusBarCallable as StatusBar,\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Container, ContainerOptions } from \"~/core/Container.js\";\nimport { Component } from \"~/core/Component.js\";\nimport { LayoutConstraints } from \"~/layout/LayoutConstraints.js\";\nimport { Tab, TabOptions } from \"~/layout/Tab.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * One entry in a {@link TabPanel}'s `tabs` options array.\n *\n * @category Components\n */\nexport interface TabEntryConfig {\n /** Label rendered in the tab button. */\n label: string;\n /** Component shown in the content area when this tab is selected. */\n component: Component;\n /** When `true`, a close button appears on the tab button. */\n closeable?: boolean;\n /** Optional registry glyph name shown leading the tab button's label. */\n glyph?: string;\n}\n\n/**\n * Construction-time options for {@link TabPanel}.\n *\n * @category Components\n */\nexport interface TabPanelOptions extends ContainerOptions {\n /** Optional initial set of tabs; each entry maps to one `addTab` call. */\n tabs?: TabEntryConfig[];\n /**\n * Optional callback fired when the user closes a tab via its close\n * button. Receives the closed tab's content component.\n */\n onTabClose?: (component: Component) => void;\n /**\n * Construction-time options for the wrapped {@link Tab} manager (strip\n * placement, width mode, scrolling, tools, compact, reorder, …). Passed\n * straight to the manager's constructor.\n */\n tabOptions?: TabOptions;\n}\n\n/**\n * A [`Container`](/api/core/classes/Container) subclass that owns an internal\n * [`Tab`](/api/layout/classes/Tab) layout manager and exposes a tab-typed\n * `addTab` / `addLazyTab` surface so consumers do not have to wire\n * `new Container({ layoutManager: new Tab() })` themselves. The bare\n * Container + Tab manager path still works unchanged; `TabPanel` is the\n * convenience entry point.\n *\n * Strip-level tuning (placement, width mode, scrolling, tools, …) is reached\n * through {@link getTab}, the typed accessor for the wrapped manager, rather\n * than a mirrored forwarder per setter.\n *\n * @example\n * ```typescript\n * import { TabPanel } from '@jimka/typescript-ui/component/container';\n *\n * const tabs = new TabPanel({\n * tabs: [\n * { label: 'Alpha', component: alphaPanel },\n * { label: 'Beta', component: betaPanel, closeable: true },\n * ],\n * onTabClose: c => console.log(\"Closed\", c.getId()),\n * });\n *\n * tabs.getTab().setSide(\"west\");\n * ```\n *\n * @category Components\n */\nclass TabPanel<TOptions extends TabPanelOptions = TabPanelOptions> extends Container<TOptions> {\n\n /**\n * Wires the panel to an internal `Tab` manager, dispatches the optional\n * `tabs` / `onTabClose` options to the relevant setters.\n *\n * @param options - Optional construction-time options applied to the panel.\n */\n constructor(options?: TOptions) {\n super(options);\n\n // Set the layout manager unconditionally — even if the caller passed\n // `layoutManager` via the options bag, `TabPanel`'s identity is the\n // `Tab` manager. Override-by-options would defeat the class. The strip\n // configuration rides in as the manager's own options bag.\n this.setLayoutManager(new Tab(options?.tabOptions));\n\n if (options?.tabs) {\n for (const entry of options.tabs) {\n this.addTab(entry.component, entry.label, { closeable: entry.closeable, glyph: entry.glyph });\n }\n }\n\n if (options?.onTabClose) {\n this.getTab().on(\"tabclose\", options.onTabClose);\n }\n }\n\n /**\n * Adds a tab to the panel's internal `Tab` manager.\n *\n * @param component - The content shown when this tab is selected.\n * @param label - The tab button's label.\n * @param options - Optional. Supports `{ closeable: true }` and a leading `glyph` name.\n *\n * @returns This panel, for method chaining.\n */\n addTab(component: Component, label: string, options?: { closeable?: boolean; glyph?: string }): this {\n const constraints = new LayoutConstraints();\n constraints.name = label;\n constraints.closeable = options?.closeable ?? false;\n constraints.glyph = options?.glyph ?? null;\n\n this.addComponent(component, constraints);\n\n return this;\n }\n\n /**\n * Registers a lazy tab. The factory runs on first activation; until then\n * the panel reserves the tab slot and shows a spinner placeholder when\n * the user selects the tab. Forwards to the wrapped `Tab.addLazyTab`.\n *\n * @param factory - Builds the content component on first activation.\n * @param label - The tab button's label.\n * @param options - Optional. Supports `{ closeable: true }` and a leading `glyph` name.\n *\n * @returns This panel, for method chaining.\n */\n addLazyTab(factory: () => Component, label: string, options?: { closeable?: boolean; glyph?: string }): this {\n const constraints = new LayoutConstraints();\n constraints.name = label;\n constraints.closeable = options?.closeable ?? false;\n constraints.glyph = options?.glyph ?? null;\n\n this.getTab().addLazyTab(factory, label, constraints);\n\n return this;\n }\n\n /**\n * Typed accessor for the internally-owned `Tab` manager. Use it to reach\n * strip-level configuration (placement, width mode, scrolling, tools,\n * events, …) and per-tab operations without casting `getLayoutManager()`.\n *\n * @returns The wrapped `Tab` instance.\n */\n getTab(): Tab {\n return this.getLayoutManager() as Tab;\n }\n}\n\nconst TabPanelCallable = callable(TabPanel);\ntype TabPanelCallable<TOptions extends TabPanelOptions = TabPanelOptions> = TabPanel<TOptions>;\nexport {\n TabPanel as _TabPanel,\n TabPanelCallable as TabPanel,\n};\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { Container, ContainerOptions } from \"~/core/Container.js\";\nimport { Component } from \"~/core/Component.js\";\nimport { Accordion, SectionToggleCallback } from \"~/layout/Accordion.js\";\nimport { AccordionConstraints } from \"~/layout/AccordionConstraints.js\";\nimport { LayoutConstraints } from \"~/layout/LayoutConstraints.js\";\nimport { callable } from \"~/core/Callable.js\";\n\n/**\n * One entry in an {@link AccordionPanel}'s `sections` options array.\n *\n * @category Components\n */\nexport interface AccordionSectionConfig {\n /** Label rendered in the section's header button. */\n label: string;\n /** Component shown inside the section's content area when expanded. */\n component: Component;\n /** Whether the section starts expanded. Defaults to `false` if omitted. */\n initiallyOpen?: boolean;\n /** Optional registry glyph name shown leading the section's header label. */\n glyph?: string;\n /** Optional per-section tool components shown in this section's header. */\n tools?: Component[];\n /**\n * Optional share of the accordion's leftover height this section fills when\n * the open sections underflow. `0`/omitted sits at preferred height; a\n * positive weight grows the section by its fraction of the total fill weight.\n * See {@link LayoutConstraints.weight}.\n */\n weight?: number;\n}\n\n/**\n * Construction-time options for {@link AccordionPanel}.\n *\n * @category Components\n */\nexport interface AccordionPanelOptions extends ContainerOptions {\n /** Optional initial sections; each entry maps to one `addSection` call. */\n sections?: AccordionSectionConfig[];\n /** Mirrors `Accordion.setSingleOpen` — only one section open at a time. */\n singleOpen?: boolean;\n /** Mirrors `Accordion.setResizable` — draggable gutters between open sections. */\n resizable?: boolean;\n /** Optional callback fired when a section opens or closes. */\n onSectionToggle?: SectionToggleCallback;\n}\n\n/**\n * A [`Container`](/api/core/classes/Container) subclass that owns an internal\n * [`Accordion`](/api/layout/classes/Accordion) layout manager and exposes a\n * section-typed `addSection` surface so consumers do not have to wire\n * `new Container({ layoutManager: new Accordion() })` themselves. The bare\n * Container + Accordion manager path still works unchanged; `AccordionPanel`\n * is the convenience entry point.\n *\n * Section operations (open/close, single-open mode, toggle events) are reached\n * through {@link getAccordion}, the typed accessor for the wrapped manager,\n * rather than a mirrored forwarder per method.\n *\n * @example\n * ```typescript\n * import { AccordionPanel } from '@jimka/typescript-ui/component/container';\n *\n * const acc = new AccordionPanel({\n * singleOpen: true,\n * sections: [\n * { label: 'Profile', component: profilePanel, initiallyOpen: true },\n * { label: 'Preferences', component: prefsPanel },\n * ],\n * });\n *\n * acc.getAccordion().openSection(1);\n * ```\n *\n * @category Components\n */\nclass AccordionPanel<TOptions extends AccordionPanelOptions = AccordionPanelOptions> extends Container<TOptions> {\n\n /**\n * Wires the panel to an internal `Accordion` manager, dispatches the\n * optional `sections` / `singleOpen` / `onSectionToggle` options to the\n * relevant setters.\n *\n * @param options - Optional construction-time options applied to the panel.\n */\n constructor(options?: TOptions) {\n super(options);\n\n // Set the layout manager unconditionally — even if the caller passed\n // `layoutManager` via the options bag, `AccordionPanel`'s identity is\n // the `Accordion` manager. Override-by-options would defeat the class.\n this.setLayoutManager(new Accordion());\n\n if (options?.singleOpen !== undefined) {\n this.getAccordion().setSingleOpen(options.singleOpen);\n }\n\n if (options?.resizable !== undefined) {\n this.getAccordion().setResizable(options.resizable);\n }\n\n if (options?.sections) {\n for (const section of options.sections) {\n this.addSection(section.component, section.label, section.initiallyOpen, section.glyph, section.tools, section.weight);\n }\n }\n\n if (options?.onSectionToggle) {\n this.getAccordion().on(\"sectiontoggle\", options.onSectionToggle);\n }\n }\n\n /**\n * Adds a section to the panel's internal `Accordion` manager.\n *\n * @param component - The content shown inside the section.\n * @param label - The section header's label.\n * @param initiallyOpen - Optional. Whether the section starts expanded; defaults to `false`.\n * @param glyph - Optional. Registry glyph name shown leading the header label.\n * @param tools - Optional. Per-section tool components for this header.\n * @param weight - Optional. Share of the accordion's leftover height this\n * section fills when the open sections underflow (see\n * {@link LayoutConstraints.weight}).\n *\n * @returns This panel, for method chaining.\n */\n addSection(component: Component, label: string, initiallyOpen?: boolean, glyph?: string, tools?: Component[], weight?: number): this {\n const constraints = new AccordionConstraints(label, initiallyOpen, glyph, tools);\n\n if (weight !== undefined) {\n constraints.weight = weight;\n }\n\n this.addComponent(component, constraints);\n\n return this;\n }\n\n /**\n * Typed accessor for the internally-owned `Accordion` manager. Use it to\n * reach section operations (open/close, single-open mode, toggle events)\n * without casting `getLayoutManager()`.\n *\n * @returns The wrapped `Accordion` instance.\n */\n getAccordion(): Accordion {\n return this.getLayoutManager() as Accordion;\n }\n}\n\nconst AccordionPanelCallable = callable(AccordionPanel);\ntype AccordionPanelCallable<TOptions extends AccordionPanelOptions = AccordionPanelOptions> = AccordionPanel<TOptions>;\nexport {\n AccordionPanel as _AccordionPanel,\n AccordionPanelCallable as AccordionPanel,\n};\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{n as e,u as t}from"../Position-B-WfZYAJ.js";import{c as n,f as r,t as i,y as a}from"../Component-BR2f0Itb.js";import{t as o}from"../ListenerBag-WPdKuuds.js";import{t as s}from"../Container-3KsvsJ6S.js";import{t as c}from"../Panel-DIsdaqc2.js";import{t as l}from"../Fit-CFIr4P9R.js";import{t as u}from"../Text-D1RrBY52.js";import{t as d}from"../IconText-BvGfu7eq.js";var f=120,p=40;function mergeLayoutOptions(...e){let t={};for(let n of e)n&&Object.assign(t,n);return t}var m={"elk.padding":`[top=34,left=12,bottom=12,right=12]`};function mapDiagramNode(e,t){if(e.children&&e.children.length>0)return{id:e.id,layoutOptions:mergeLayoutOptions(m,e.layoutOptions),children:e.children.map(e=>mapDiagramNode(e,t))};let n=t.get(e.id);return{id:e.id,width:e.width??n?.width??f,height:e.height??n?.height??p,layoutOptions:e.layoutOptions,ports:e.ports?.map(e=>({id:e.id,x:e.x,y:e.y,width:e.width,height:e.height,layoutOptions:e.side===void 0?void 0:{"elk.port.side":e.side}}))}}var h={"elk.hierarchyHandling":`INCLUDE_CHILDREN`};function buildElkGraph(e,t,n){let r=e.nodes.map(e=>mapDiagramNode(e,t)),i=e.edges.map(e=>({id:e.id,sources:[e.sourcePort??e.source],targets:[e.targetPort??e.target]}));return{id:`root`,layoutOptions:mergeLayoutOptions(h,n,e.layoutOptions),children:r,edges:i}}function flattenElkNode(e,t,n,r){let i=t+(e.x??0),a=n+(e.y??0);r.push({id:e.id,x:i,y:a,width:e.width??0,height:e.height??0});for(let t of e.children??[])flattenElkNode(t,i,a,r)}function collectElkEdges(e,t){for(let n of e.edges??[])t.push({edge:n,container:n.container??e.id});for(let n of e.children??[])collectElkEdges(n,t)}function offsetSections(e,t){if(t.x===0&&t.y===0)return e;let shift=e=>({x:e.x+t.x,y:e.y+t.y});return e.map(e=>({startPoint:shift(e.startPoint),endPoint:shift(e.endPoint),bendPoints:e.bendPoints?.map(shift)}))}function mapElkResult(e){let t=[];for(let n of e.children??[])flattenElkNode(n,0,0,t);let n=new Map([[e.id,{x:0,y:0}]]);for(let e of t)n.set(e.id,{x:e.x,y:e.y});let r=[];return collectElkEdges(e,r),{nodes:t,edges:r.map(({edge:e,container:t})=>({id:e.id,sections:offsetSections(e.sections??[],n.get(t)??{x:0,y:0})})),width:e.width??0,height:e.height??0}}var ElkLayoutEngine=class{_elk=null;_workerUrl;constructor(e){this._workerUrl=e}async layout(e,t,n){let r=await this.ensureElk(),i=buildElkGraph(e,t,n);return mapElkResult(await r.layout(i))}async ensureElk(){if(this._elk)return this._elk;let{default:e}=await import(`elkjs/lib/elk.bundled.js`),t=this._workerUrl?new e({workerUrl:this._workerUrl}):new e;return this._elk=t,t}},g={insets:new e(4,8,4,8)},_=`4px`,DiagramNode=class extends c{_content;get selectedStyleRule(){return this._selectedStyleRule??=this.createStyleRule(`.selected`)}constructor(e){super(e,{...g,layoutManager:new l}),this.setBackgroundColor(`var(--ts-ui-diagram-node-bg, var(--ts-ui-button-bg, rgb(245, 245, 245)))`),this.setBorder(`1px solid var(--ts-ui-border-color, rgb(180, 180, 180))`),this.setBorderRadius(_),this.setCursor(`pointer`),this.selectedStyleRule.set(`borderColor`,`var(--ts-ui-accent-color, rgb(30, 100, 200))`),this.selectedStyleRule.set(`backgroundColor`,`var(--ts-ui-diagram-node-selected-bg, var(--ts-ui-table-row-selected, rgba(30, 100, 200, 0.15)))`),this.buildContent(this._options.glyph,this._options.label??``),this._options.selected!==void 0&&this.setSelected(this._options.selected)}applyOptions(e){return super.applyOptions(e),e.label!==void 0&&(this._options.label=e.label),e.glyph!==void 0&&(this._options.glyph=e.glyph),e.selected!==void 0&&(this._options.selected=e.selected),this}buildContent(e,t){this._content&&this.removeComponent(this._content),this._content=e===void 0?new u(t):new d(e,t),this._content.setPointerEvents(`none`),this.addComponent(this._content)}setLabel(e){return this._options.label=e,this._content.setText(e),this}getLabel(){return this._options.label??null}setSelected(e){this._options.selected=e;let n=this.getElement();return n&&t.sink.apply(n,{toggleClass:{selected:e}}),this}isSelected(){return this._options.selected??!1}init(e){super.init(e);let n=e||this.getElement();return n&&this.isSelected()&&t.sink.apply(n,{toggleClass:{selected:!0}}),this}},v=r(DiagramNode),y=`4px`,b=6,DiagramGroupNode=class extends c{_header;constructor(e){super(e,{layoutManager:new n}),this.setBackgroundColor(`var(--ts-ui-diagram-group-bg, rgba(120, 120, 120, 0.08))`),this.setBorder(`1px solid var(--ts-ui-diagram-group-border, var(--ts-ui-border-color, rgb(180, 180, 180)))`),this.setBorderRadius(y);let t=this._options.label??``;this._header=this._options.glyph===void 0?new u(t):new d(this._options.glyph,t),this._header.setPointerEvents(`none`),this._header.setX(b),this._header.setY(b),this.addComponent(this._header)}applyOptions(e){return super.applyOptions(e),e.label!==void 0&&(this._options.label=e.label),e.glyph!==void 0&&(this._options.glyph=e.glyph),this}setLabel(e){return this._options.label=e,this._header.setText(e),this}getLabel(){return this._options.label??null}},x=r(DiagramGroupNode),S=`http://www.w3.org/2000/svg`,C=`var(--ts-ui-diagram-edge, var(--ts-ui-border-color, rgb(120, 120, 120)))`,w=`1.5`,T=`12`,E=`var(--ts-ui-diagram-bg, var(--ts-ui-panel-bg, rgb(255, 255, 255)))`,D=`4`,O=8,k=`6 4`,A=[`one`,`zeroOrOne`,`oneOrMany`,`zeroOrMany`],j={one:{width:12,height:12,refX:12,refY:6,parts:[{tag:`path`,attrs:{d:`M 4 0 L 4 12 M 8 0 L 8 12`}}]},zeroOrOne:{width:16,height:12,refX:16,refY:6,parts:[{tag:`path`,attrs:{d:`M 12 0 L 12 12`}},{tag:`circle`,attrs:{cx:`6`,cy:`6`,r:`4`}}]},oneOrMany:{width:16,height:12,refX:16,refY:6,parts:[{tag:`path`,attrs:{d:`M 0 6 L 12 0 M 0 6 L 12 6 M 0 6 L 12 12`}},{tag:`path`,attrs:{d:`M 14 0 L 14 12`}}]},zeroOrMany:{width:18,height:12,refX:18,refY:6,parts:[{tag:`path`,attrs:{d:`M 6 6 L 18 0 M 6 6 L 18 6 M 6 6 L 18 12`}},{tag:`circle`,attrs:{cx:`3`,cy:`6`,r:`3`}}]}};function labelPoint(e){let t=e[0];return t?midpointAlong([t.startPoint,...t.bendPoints??[],t.endPoint]):{x:0,y:0}}function midpointAlong(e){let segmentLength=(e,t)=>Math.hypot(t.x-e.x,t.y-e.y),t=0;for(let n=1;n<e.length;n++)t+=segmentLength(e[n-1],e[n]);let n=t/2;for(let t=1;t<e.length;t++){let r=segmentLength(e[t-1],e[t]);if(r>=n){let i=r===0?0:n/r;return{x:e[t-1].x+(e[t].x-e[t-1].x)*i,y:e[t-1].y+(e[t].y-e[t-1].y)*i}}n-=r}return e[e.length-1]}function buildPathData(e){let t=[];for(let n of e){t.push(`M ${n.startPoint.x} ${n.startPoint.y}`);for(let e of n.bendPoints??[])t.push(`L ${e.x} ${e.y}`);t.push(`L ${n.endPoint.x} ${n.endPoint.y}`)}return t.join(` `)}var DiagramEdgeLayer=class extends i{_edges=[];_pathHandles=[];_markerId;_crowsFootMarkerIds;constructor(e){super(e),this._markerId=`${this.getId()}-arrow`,this._crowsFootMarkerIds=Object.fromEntries(A.map(e=>[e,`${this.getId()}-${e}`])),this.setPointerEvents(`none`)}markerUrl(e){if(e)return`url(#${e===`arrow`?this._markerId:this._crowsFootMarkerIds[e]})`}setEdges(e){return this._edges=e,this.rebuildPaths(),this}createRootElement(){let e=t.sink.createElementNS(S,`svg`),n=t.sink.createElementNS(S,`defs`);this.createArrowMarker(n);for(let e of A)this.createCrowsFootMarker(n,e);return t.sink.appendChild(e,n),this.trackHandle(n),e}createArrowMarker(e){let n=t.sink.createElementNS(S,`marker`);t.sink.apply(n,{setAttr:{id:this._markerId,markerWidth:String(O),markerHeight:String(O),refX:String(O),refY:String(O/2),orient:`auto-start-reverse`,markerUnits:`userSpaceOnUse`}});let r=t.sink.createElementNS(S,`path`);t.sink.apply(r,{setAttr:{d:`M 0 0 L ${O} ${O/2} L 0 ${O} z`,fill:C}}),t.sink.appendChild(n,r),t.sink.appendChild(e,n),this.trackHandle(n),this.trackHandle(r)}createCrowsFootMarker(e,n){let r=j[n],i=t.sink.createElementNS(S,`marker`);t.sink.apply(i,{setAttr:{id:this._crowsFootMarkerIds[n],markerWidth:String(r.width),markerHeight:String(r.height),refX:String(r.refX),refY:String(r.refY),orient:`auto-start-reverse`,markerUnits:`userSpaceOnUse`}}),t.sink.appendChild(e,i),this.trackHandle(i);for(let e of r.parts){let n=t.sink.createElementNS(S,e.tag);t.sink.apply(n,{setAttr:{...e.attrs,fill:`none`,stroke:C}}),t.sink.appendChild(i,n),this.trackHandle(n)}}render(){let e=super.render();return this.rebuildPaths(),e}rebuildPaths(){let e=this.getElement();if(e){for(let n of this._pathHandles)t.sink.removeChild(e,n),this.untrackHandle(n),t.sink.release(n);this._pathHandles=[];for(let n of this._edges){let r=buildPathData(n.sections);if(!r)continue;let i=t.sink.createElementNS(S,`path`),a=n.style,o=a?this.markerUrl(a.endMarker):this.markerUrl(`arrow`),s=a?this.markerUrl(a.startMarker):void 0,c={d:r,fill:`none`,stroke:a?.stroke??C,"stroke-width":w};o&&(c[`marker-end`]=o),s&&(c[`marker-start`]=s),a?.dashed&&(c[`stroke-dasharray`]=k),t.sink.apply(i,{setAttr:c}),t.sink.appendChild(e,i),this.trackHandle(i),this._pathHandles.push(i),a?.label&&this.drawLabel(e,n,a.label)}}}drawLabel(e,n,r){let i=labelPoint(n.sections),a=t.sink.createElementNS(S,`text`);t.sink.apply(a,{setAttr:{x:String(i.x),y:String(i.y),fill:C,stroke:E,"stroke-width":D,"stroke-linejoin":`round`,"paint-order":`stroke`,"font-size":T,"text-anchor":`middle`,"dominant-baseline":`central`},text:r}),t.sink.appendChild(e,a),this.trackHandle(a),this._pathHandles.push(a)}},M=r(DiagramEdgeLayer),N=1,P=.25,F=4,I=1.1,L=0,R=0,z=1,B=2,DiagramView=class extends c{_contentHost;_edgeLayer;_engine;_nodeComponents=new Map;_nodeData=new Map;_containerIds=new Set;_selection=[];_layoutGeneration=0;_graphWidth=0;_graphHeight=0;_listeners=new o;_panning=!1;_panStartX=0;_panStartY=0;_panScrollLeft=0;_panScrollTop=0;constructor(e){super(e,{zoom:N,minZoom:P,maxZoom:F}),this.setAutoScroll(`auto`),this._engine=this.createEngine(),this._contentHost=new s({layoutManager:new n,overflow:`visible`}),this._contentHost.setTransformOrigin(`0 0`),this.addComponent(this._contentHost),this._edgeLayer=new M,this._contentHost.addComponent(this._edgeLayer),this.applyListeners(e?.listeners),this._options.zoom!==void 0&&this.setZoom(this._options.zoom),this._options.data&&this.setData(this._options.data)}createEngine(){return new ElkLayoutEngine(this._options.elkWorkerUrl)}applyOptions(e){return super.applyOptions(e),e.data!==void 0&&(this._options.data=e.data),e.nodeRenderer!==void 0&&(this._options.nodeRenderer=e.nodeRenderer),e.groupRenderer!==void 0&&(this._options.groupRenderer=e.groupRenderer),e.layoutOptions!==void 0&&(this._options.layoutOptions=e.layoutOptions),e.elkWorkerUrl!==void 0&&(this._options.elkWorkerUrl=e.elkWorkerUrl),e.minZoom!==void 0&&(this._options.minZoom=e.minZoom),e.maxZoom!==void 0&&(this._options.maxZoom=e.maxZoom),e.zoom!==void 0&&(this._options.zoom=e.zoom),this}setData(e){return this._options.data=e,this.rebuildNodes(e),this.relayout(e),this}getData(){return this._options.data??null}rebuildNodes(e){for(let e of this._nodeComponents.values())this._contentHost.removeComponent(e);this._nodeComponents.clear(),this._nodeData.clear(),this._containerIds.clear(),this._selection=[],this._edgeLayer.setEdges([]);let t=this._options.nodeRenderer??(e=>new v({label:e.label,glyph:e.glyph})),n=this._options.groupRenderer??(e=>new x({label:e.label,glyph:e.glyph})),build=e=>{for(let r of e){let e=(r.children?.length??0)>0,i=e?n(r):t(r);this._nodeComponents.set(r.id,i),this._nodeData.set(r.id,r),this._contentHost.addComponent(i),e&&(this._containerIds.add(r.id),build(r.children))}};build(e.nodes)}relayout(e){let t=this.collectNodeSizes(e);this._layoutGeneration+=1;let n=this._layoutGeneration;this._engine.layout(e,t,this._options.layoutOptions).then(e=>this.applyLayout(e,n)).catch(()=>this.handleLayoutFailure(n))}collectNodeSizes(e){let t=new Map,collect=e=>{for(let n of e){let e=this._nodeComponents.get(n.id)?.getPreferredSize();t.set(n.id,{width:n.width??e?.width??0,height:n.height??e?.height??0}),n.children&&n.children.length>0&&collect(n.children)}};return collect(e.nodes),t}applyLayout(e,t){if(t===this._layoutGeneration){for(let t of e.nodes){let e=this._nodeComponents.get(t.id);e&&(e.setPreferredSize(t.width,t.height),e.setX(t.x),e.setY(t.y))}this._graphWidth=e.width,this._graphHeight=e.height,this._edgeLayer.setX(0),this._edgeLayer.setY(0),this._edgeLayer.setPreferredSize(e.width,e.height),this._edgeLayer.setEdges(this.joinEdgeStyles(e.edges)),this.applyContainerZIndex(),this.applyZoomToHost(),this.scheduleLayout(),this.emit(`layout`)}}applyContainerZIndex(){if(this._containerIds.size===0){this._edgeLayer.setZIndex(L);return}this._edgeLayer.setZIndex(z);for(let[e,t]of this._nodeComponents)t.setZIndex(this._containerIds.has(e)?R:B)}joinEdgeStyles(e){let t=new Map(this._options.data?.edges.map(e=>[e.id,e])??[]);return e.map(e=>({...e,style:t.get(e.id)?.style}))}handleLayoutFailure(e){if(e===this._layoutGeneration){for(let e of this._nodeComponents.values())this._contentHost.removeComponent(e);this._nodeComponents.clear(),this._nodeData.clear()}}applyZoomToHost(){let e=this.getZoom();this._contentHost.setTransform(`scale(${e})`);let t=Math.min(e,1);this._contentHost.setPreferredSize(this._graphWidth*t,this._graphHeight*t)}getZoom(){return this._options.zoom??this._defaultOptions.zoom??N}setZoom(e){return this._options.zoom=this.clampZoom(e),this.applyZoomToHost(),this}zoomToFit(){if(this._graphWidth<=0||this._graphHeight<=0)return this;let e=this.getWidth()/this._graphWidth,t=this.getHeight()/this._graphHeight;return this.setZoom(Math.min(e,t)),this}clampZoom(e){let t=this._options.minZoom??this._defaultOptions.minZoom??P,n=this._options.maxZoom??this._defaultOptions.maxZoom??F;return Math.max(t,Math.min(n,e))}getSelection(){return[...this._selection]}selectNode(e){return this.setSelection(e),this}revealNode(e){let t=this._nodeComponents.get(e);if(!t)return this;let n=this.getZoom(),r=(t.getX()+t.getWidth()/2)*n,i=(t.getY()+t.getHeight()/2)*n;return this.setScrollLeft(Math.max(0,r-this.getWidth()/2)),this.setScrollTop(Math.max(0,i-this.getHeight()/2)),this}setSelection(e){for(let e of this._nodeComponents.values())this.applySelectedVisual(e,!1);let t=e===null?void 0:this._nodeData.get(e);t?(this._selection=[t],this.applySelectedVisual(this._nodeComponents.get(e),!0)):this._selection=[]}applySelectedVisual(e,t){e.setSelected?.(t)}on(e,t){return this._listeners.add(e,t),this}off(e,t){return this._listeners.remove(e,t),this}emit(e,...t){this._listeners.fire(e,...t)}init(e){return super.init(e),a.addSubtreeListener(this,`click`,this._handleClick),a.addSubtreeListener(this,`dblclick`,this._handleDoubleClick),a.addListener(this,`wheel`,this._handleWheel),a.addListener(this,`pointerdown`,this._handlePointerDown),a.addListener(this,`pointermove`,this._handlePointerMove),a.addListener(this,`pointerup`,this._handlePointerUp),this}_handleClick(e){let t=this.nodeIdAt(e.target);t===null?this._selection.length>0&&(this.setSelection(null),this.emit(`selection`,this.getSelection())):(this.setSelection(t),this.emit(`selection`,this.getSelection()))}_handleDoubleClick(e){let t=this.nodeIdAt(e.target);if(t===null)return;let n=this._nodeData.get(t);n!==void 0&&this.emit(`activate`,n)}nodeIdAt(e){if(e===null)return null;let n=t.source.intern(e);for(let[e,r]of this._nodeComponents){let i=r.getElement();if(i&&(i===n||t.source.contains(i,n)))return e}return null}_handleWheel(e){e.preventDefault();let n=this.getZoom(),r=this.clampZoom(n*(e.deltaY<0?I:1/I));if(r===n)return;let i=t.source.getViewportRect(this),a=e.clientX-i.left,o=e.clientY-i.top,s=(a+this.getScrollLeft())/n,c=(o+this.getScrollTop())/n;this.setZoom(r),this.setScrollLeft(s*r-a),this.setScrollTop(c*r-o)}_handlePointerDown(e){e.button===0&&(this._panning=!0,this._panStartX=e.clientX,this._panStartY=e.clientY,this._panScrollLeft=this.getScrollLeft(),this._panScrollTop=this.getScrollTop())}_handlePointerMove(e){this._panning&&(this.setScrollLeft(this._panScrollLeft-(e.clientX-this._panStartX)),this.setScrollTop(this._panScrollTop-(e.clientY-this._panStartY)))}_handlePointerUp(){this._panning=!1}},V=r(DiagramView);export{M as DiagramEdgeLayer,x as DiagramGroupNode,v as DiagramNode,V as DiagramView,ElkLayoutEngine};
1
+ import{t as e}from"../DOM-DeF_dWe2.js";import{n as t}from"../Position-DJKB0N3l.js";import{c as n,f as r,t as i,y as a}from"../Component-Bs6TvaGZ.js";import{t as o}from"../ListenerBag-WPdKuuds.js";import{t as s}from"../Container-CzyZeMyZ.js";import{t as c}from"../Panel-DApa7vA6.js";import{t as l}from"../Fit-CUS1JXy3.js";import{t as u}from"../Text-DJoUHukb.js";import{t as d}from"../IconText-BXJK0MOq.js";var f=120,p=40;function mergeLayoutOptions(...e){let t={};for(let n of e)n&&Object.assign(t,n);return t}var m={"elk.padding":`[top=34,left=12,bottom=12,right=12]`};function mapDiagramNode(e,t){if(e.children&&e.children.length>0)return{id:e.id,layoutOptions:mergeLayoutOptions(m,e.layoutOptions),children:e.children.map(e=>mapDiagramNode(e,t))};let n=t.get(e.id);return{id:e.id,width:e.width??n?.width??f,height:e.height??n?.height??p,layoutOptions:e.layoutOptions,ports:e.ports?.map(e=>({id:e.id,x:e.x,y:e.y,width:e.width,height:e.height,layoutOptions:e.side===void 0?void 0:{"elk.port.side":e.side}}))}}var h={"elk.hierarchyHandling":`INCLUDE_CHILDREN`};function buildElkGraph(e,t,n){let r=e.nodes.map(e=>mapDiagramNode(e,t)),i=e.edges.map(e=>({id:e.id,sources:[e.sourcePort??e.source],targets:[e.targetPort??e.target]}));return{id:`root`,layoutOptions:mergeLayoutOptions(h,n,e.layoutOptions),children:r,edges:i}}function flattenElkNode(e,t,n,r){let i=t+(e.x??0),a=n+(e.y??0);r.push({id:e.id,x:i,y:a,width:e.width??0,height:e.height??0});for(let t of e.children??[])flattenElkNode(t,i,a,r)}function collectElkEdges(e,t){for(let n of e.edges??[])t.push({edge:n,container:n.container??e.id});for(let n of e.children??[])collectElkEdges(n,t)}function offsetSections(e,t){if(t.x===0&&t.y===0)return e;let shift=e=>({x:e.x+t.x,y:e.y+t.y});return e.map(e=>({startPoint:shift(e.startPoint),endPoint:shift(e.endPoint),bendPoints:e.bendPoints?.map(shift)}))}function mapElkResult(e){let t=[];for(let n of e.children??[])flattenElkNode(n,0,0,t);let n=new Map([[e.id,{x:0,y:0}]]);for(let e of t)n.set(e.id,{x:e.x,y:e.y});let r=[];return collectElkEdges(e,r),{nodes:t,edges:r.map(({edge:e,container:t})=>({id:e.id,sections:offsetSections(e.sections??[],n.get(t)??{x:0,y:0})})),width:e.width??0,height:e.height??0}}var ElkLayoutEngine=class{_elk=null;_workerUrl;constructor(e){this._workerUrl=e}async layout(e,t,n){let r=await this.ensureElk(),i=buildElkGraph(e,t,n);return mapElkResult(await r.layout(i))}async ensureElk(){if(this._elk)return this._elk;let{default:e}=await import(`elkjs/lib/elk.bundled.js`),t=this._workerUrl?new e({workerUrl:this._workerUrl}):new e;return this._elk=t,t}},g={insets:new t(4,8,4,8)},_=`4px`,DiagramNode=class extends c{_content;get selectedStyleRule(){return this._selectedStyleRule??=this.createStyleRule(`.selected`)}constructor(e){super(e,{...g,layoutManager:new l}),this.setBackgroundColor(`var(--ts-ui-diagram-node-bg, var(--ts-ui-button-bg, rgb(245, 245, 245)))`),this.setBorder(`1px solid var(--ts-ui-border-color, rgb(180, 180, 180))`),this.setBorderRadius(_),this.setCursor(`pointer`),this.selectedStyleRule.set(`borderColor`,`var(--ts-ui-accent-color, rgb(30, 100, 200))`),this.selectedStyleRule.set(`backgroundColor`,`var(--ts-ui-diagram-node-selected-bg, var(--ts-ui-table-row-selected, rgba(30, 100, 200, 0.15)))`),this.buildContent(this._options.glyph,this._options.label??``),this._options.selected!==void 0&&this.setSelected(this._options.selected)}applyOptions(e){return super.applyOptions(e),e.label!==void 0&&(this._options.label=e.label),e.glyph!==void 0&&(this._options.glyph=e.glyph),e.selected!==void 0&&(this._options.selected=e.selected),this}buildContent(e,t){this._content&&this.removeComponent(this._content),this._content=e===void 0?new u(t):new d(e,t),this._content.setPointerEvents(`none`),this.addComponent(this._content)}setLabel(e){return this._options.label=e,this._content.setText(e),this}getLabel(){return this._options.label??null}setSelected(t){this._options.selected=t;let n=this.getElement();return n&&e.sink.apply(n,{toggleClass:{selected:t}}),this}isSelected(){return this._options.selected??!1}init(t){super.init(t);let n=t||this.getElement();return n&&this.isSelected()&&e.sink.apply(n,{toggleClass:{selected:!0}}),this}},v=r(DiagramNode),y=`4px`,b=6,DiagramGroupNode=class extends c{_header;constructor(e){super(e,{layoutManager:new n}),this.setBackgroundColor(`var(--ts-ui-diagram-group-bg, rgba(120, 120, 120, 0.08))`),this.setBorder(`1px solid var(--ts-ui-diagram-group-border, var(--ts-ui-border-color, rgb(180, 180, 180)))`),this.setBorderRadius(y);let t=this._options.label??``;this._header=this._options.glyph===void 0?new u(t):new d(this._options.glyph,t),this._header.setPointerEvents(`none`),this._header.setX(b),this._header.setY(b),this.addComponent(this._header)}applyOptions(e){return super.applyOptions(e),e.label!==void 0&&(this._options.label=e.label),e.glyph!==void 0&&(this._options.glyph=e.glyph),this}setLabel(e){return this._options.label=e,this._header.setText(e),this}getLabel(){return this._options.label??null}},x=r(DiagramGroupNode),S=`http://www.w3.org/2000/svg`,C=`var(--ts-ui-diagram-edge, var(--ts-ui-border-color, rgb(120, 120, 120)))`,w=`1.5`,T=`12`,E=`var(--ts-ui-diagram-bg, var(--ts-ui-panel-bg, rgb(255, 255, 255)))`,D=`4`,O=8,k=`6 4`,A=[`one`,`zeroOrOne`,`oneOrMany`,`zeroOrMany`],j={one:{width:12,height:12,refX:12,refY:6,parts:[{tag:`path`,attrs:{d:`M 4 0 L 4 12 M 8 0 L 8 12`}}]},zeroOrOne:{width:16,height:12,refX:16,refY:6,parts:[{tag:`path`,attrs:{d:`M 12 0 L 12 12`}},{tag:`circle`,attrs:{cx:`6`,cy:`6`,r:`4`}}]},oneOrMany:{width:16,height:12,refX:16,refY:6,parts:[{tag:`path`,attrs:{d:`M 0 6 L 12 0 M 0 6 L 12 6 M 0 6 L 12 12`}},{tag:`path`,attrs:{d:`M 14 0 L 14 12`}}]},zeroOrMany:{width:18,height:12,refX:18,refY:6,parts:[{tag:`path`,attrs:{d:`M 6 6 L 18 0 M 6 6 L 18 6 M 6 6 L 18 12`}},{tag:`circle`,attrs:{cx:`3`,cy:`6`,r:`3`}}]}};function labelPoint(e){let t=e[0];return t?midpointAlong([t.startPoint,...t.bendPoints??[],t.endPoint]):{x:0,y:0}}function midpointAlong(e){let segmentLength=(e,t)=>Math.hypot(t.x-e.x,t.y-e.y),t=0;for(let n=1;n<e.length;n++)t+=segmentLength(e[n-1],e[n]);let n=t/2;for(let t=1;t<e.length;t++){let r=segmentLength(e[t-1],e[t]);if(r>=n){let i=r===0?0:n/r;return{x:e[t-1].x+(e[t].x-e[t-1].x)*i,y:e[t-1].y+(e[t].y-e[t-1].y)*i}}n-=r}return e[e.length-1]}function buildPathData(e){let t=[];for(let n of e){t.push(`M ${n.startPoint.x} ${n.startPoint.y}`);for(let e of n.bendPoints??[])t.push(`L ${e.x} ${e.y}`);t.push(`L ${n.endPoint.x} ${n.endPoint.y}`)}return t.join(` `)}var DiagramEdgeLayer=class extends i{_edges=[];_pathHandles=[];_markerId;_crowsFootMarkerIds;constructor(e){super(e),this._markerId=`${this.getId()}-arrow`,this._crowsFootMarkerIds=Object.fromEntries(A.map(e=>[e,`${this.getId()}-${e}`])),this.setPointerEvents(`none`)}markerUrl(e){if(e)return`url(#${e===`arrow`?this._markerId:this._crowsFootMarkerIds[e]})`}setEdges(e){return this._edges=e,this.rebuildPaths(),this}createRootElement(){let t=e.sink.createElementNS(S,`svg`),n=e.sink.createElementNS(S,`defs`);this.createArrowMarker(n);for(let e of A)this.createCrowsFootMarker(n,e);return e.sink.appendChild(t,n),this.trackHandle(n),t}createArrowMarker(t){let n=e.sink.createElementNS(S,`marker`);e.sink.apply(n,{setAttr:{id:this._markerId,markerWidth:String(O),markerHeight:String(O),refX:String(O),refY:String(O/2),orient:`auto-start-reverse`,markerUnits:`userSpaceOnUse`}});let r=e.sink.createElementNS(S,`path`);e.sink.apply(r,{setAttr:{d:`M 0 0 L ${O} ${O/2} L 0 ${O} z`,fill:C}}),e.sink.appendChild(n,r),e.sink.appendChild(t,n),this.trackHandle(n),this.trackHandle(r)}createCrowsFootMarker(t,n){let r=j[n],i=e.sink.createElementNS(S,`marker`);e.sink.apply(i,{setAttr:{id:this._crowsFootMarkerIds[n],markerWidth:String(r.width),markerHeight:String(r.height),refX:String(r.refX),refY:String(r.refY),orient:`auto-start-reverse`,markerUnits:`userSpaceOnUse`}}),e.sink.appendChild(t,i),this.trackHandle(i);for(let t of r.parts){let n=e.sink.createElementNS(S,t.tag);e.sink.apply(n,{setAttr:{...t.attrs,fill:`none`,stroke:C}}),e.sink.appendChild(i,n),this.trackHandle(n)}}render(){let e=super.render();return this.rebuildPaths(),e}rebuildPaths(){let t=this.getElement();if(t){for(let n of this._pathHandles)e.sink.removeChild(t,n),this.untrackHandle(n),e.sink.release(n);this._pathHandles=[];for(let n of this._edges){let r=buildPathData(n.sections);if(!r)continue;let i=e.sink.createElementNS(S,`path`),a=n.style,o=a?this.markerUrl(a.endMarker):this.markerUrl(`arrow`),s=a?this.markerUrl(a.startMarker):void 0,c={d:r,fill:`none`,stroke:a?.stroke??C,"stroke-width":w};o&&(c[`marker-end`]=o),s&&(c[`marker-start`]=s),a?.dashed&&(c[`stroke-dasharray`]=k),e.sink.apply(i,{setAttr:c}),e.sink.appendChild(t,i),this.trackHandle(i),this._pathHandles.push(i),a?.label&&this.drawLabel(t,n,a.label)}}}drawLabel(t,n,r){let i=labelPoint(n.sections),a=e.sink.createElementNS(S,`text`);e.sink.apply(a,{setAttr:{x:String(i.x),y:String(i.y),fill:C,stroke:E,"stroke-width":D,"stroke-linejoin":`round`,"paint-order":`stroke`,"font-size":T,"text-anchor":`middle`,"dominant-baseline":`central`},text:r}),e.sink.appendChild(t,a),this.trackHandle(a),this._pathHandles.push(a)}},M=r(DiagramEdgeLayer),N=1,P=.25,F=4,I=1.1,L=0,R=0,z=1,B=2,DiagramView=class extends c{_contentHost;_edgeLayer;_engine;_nodeComponents=new Map;_nodeData=new Map;_containerIds=new Set;_selection=[];_layoutGeneration=0;_graphWidth=0;_graphHeight=0;_listeners=new o;_panning=!1;_panStartX=0;_panStartY=0;_panScrollLeft=0;_panScrollTop=0;constructor(e){super(e,{zoom:N,minZoom:P,maxZoom:F}),this.setAutoScroll(`auto`),this._engine=this.createEngine(),this._contentHost=new s({layoutManager:new n,overflow:`visible`}),this._contentHost.setTransformOrigin(`0 0`),this.addComponent(this._contentHost),this._edgeLayer=new M,this._contentHost.addComponent(this._edgeLayer),this.applyListeners(e?.listeners),this._options.zoom!==void 0&&this.setZoom(this._options.zoom),this._options.data&&this.setData(this._options.data)}createEngine(){return new ElkLayoutEngine(this._options.elkWorkerUrl)}applyOptions(e){return super.applyOptions(e),e.data!==void 0&&(this._options.data=e.data),e.nodeRenderer!==void 0&&(this._options.nodeRenderer=e.nodeRenderer),e.groupRenderer!==void 0&&(this._options.groupRenderer=e.groupRenderer),e.layoutOptions!==void 0&&(this._options.layoutOptions=e.layoutOptions),e.elkWorkerUrl!==void 0&&(this._options.elkWorkerUrl=e.elkWorkerUrl),e.minZoom!==void 0&&(this._options.minZoom=e.minZoom),e.maxZoom!==void 0&&(this._options.maxZoom=e.maxZoom),e.zoom!==void 0&&(this._options.zoom=e.zoom),this}setData(e){return this._options.data=e,this.rebuildNodes(e),this.relayout(e),this}getData(){return this._options.data??null}rebuildNodes(e){for(let e of this._nodeComponents.values())this._contentHost.removeComponent(e);this._nodeComponents.clear(),this._nodeData.clear(),this._containerIds.clear(),this._selection=[],this._edgeLayer.setEdges([]);let t=this._options.nodeRenderer??(e=>new v({label:e.label,glyph:e.glyph})),n=this._options.groupRenderer??(e=>new x({label:e.label,glyph:e.glyph})),build=e=>{for(let r of e){let e=(r.children?.length??0)>0,i=e?n(r):t(r);this._nodeComponents.set(r.id,i),this._nodeData.set(r.id,r),this._contentHost.addComponent(i),e&&(this._containerIds.add(r.id),build(r.children))}};build(e.nodes)}relayout(e){let t=this.collectNodeSizes(e);this._layoutGeneration+=1;let n=this._layoutGeneration;this._engine.layout(e,t,this._options.layoutOptions).then(e=>this.applyLayout(e,n)).catch(()=>this.handleLayoutFailure(n))}collectNodeSizes(e){let t=new Map,collect=e=>{for(let n of e){let e=this._nodeComponents.get(n.id)?.getPreferredSize();t.set(n.id,{width:n.width??e?.width??0,height:n.height??e?.height??0}),n.children&&n.children.length>0&&collect(n.children)}};return collect(e.nodes),t}applyLayout(e,t){if(t===this._layoutGeneration){for(let t of e.nodes){let e=this._nodeComponents.get(t.id);e&&(e.setPreferredSize(t.width,t.height),e.setX(t.x),e.setY(t.y))}this._graphWidth=e.width,this._graphHeight=e.height,this._edgeLayer.setX(0),this._edgeLayer.setY(0),this._edgeLayer.setPreferredSize(e.width,e.height),this._edgeLayer.setEdges(this.joinEdgeStyles(e.edges)),this.applyContainerZIndex(),this.applyZoomToHost(),this.scheduleLayout(),this.emit(`layout`)}}applyContainerZIndex(){if(this._containerIds.size===0){this._edgeLayer.setZIndex(L);return}this._edgeLayer.setZIndex(z);for(let[e,t]of this._nodeComponents)t.setZIndex(this._containerIds.has(e)?R:B)}joinEdgeStyles(e){let t=new Map(this._options.data?.edges.map(e=>[e.id,e])??[]);return e.map(e=>({...e,style:t.get(e.id)?.style}))}handleLayoutFailure(e){if(e===this._layoutGeneration){for(let e of this._nodeComponents.values())this._contentHost.removeComponent(e);this._nodeComponents.clear(),this._nodeData.clear()}}applyZoomToHost(){let e=this.getZoom();this._contentHost.setTransform(`scale(${e})`);let t=Math.min(e,1);this._contentHost.setPreferredSize(this._graphWidth*t,this._graphHeight*t)}getZoom(){return this._options.zoom??this._defaultOptions.zoom??N}setZoom(e){return this._options.zoom=this.clampZoom(e),this.applyZoomToHost(),this}zoomToFit(){if(this._graphWidth<=0||this._graphHeight<=0)return this;let e=this.getWidth()/this._graphWidth,t=this.getHeight()/this._graphHeight;return this.setZoom(Math.min(e,t)),this}clampZoom(e){let t=this._options.minZoom??this._defaultOptions.minZoom??P,n=this._options.maxZoom??this._defaultOptions.maxZoom??F;return Math.max(t,Math.min(n,e))}getSelection(){return[...this._selection]}selectNode(e){return this.setSelection(e),this}revealNode(e){let t=this._nodeComponents.get(e);if(!t)return this;let n=this.getZoom(),r=(t.getX()+t.getWidth()/2)*n,i=(t.getY()+t.getHeight()/2)*n;return this.setScrollLeft(Math.max(0,r-this.getWidth()/2)),this.setScrollTop(Math.max(0,i-this.getHeight()/2)),this}setSelection(e){for(let e of this._nodeComponents.values())this.applySelectedVisual(e,!1);let t=e===null?void 0:this._nodeData.get(e);t?(this._selection=[t],this.applySelectedVisual(this._nodeComponents.get(e),!0)):this._selection=[]}applySelectedVisual(e,t){e.setSelected?.(t)}on(e,t){return this._listeners.add(e,t),this}off(e,t){return this._listeners.remove(e,t),this}emit(e,...t){this._listeners.fire(e,...t)}init(e){return super.init(e),a.addSubtreeListener(this,`click`,this._handleClick),a.addSubtreeListener(this,`dblclick`,this._handleDoubleClick),a.addListener(this,`wheel`,this._handleWheel),a.addListener(this,`pointerdown`,this._handlePointerDown),a.addListener(this,`pointermove`,this._handlePointerMove),a.addListener(this,`pointerup`,this._handlePointerUp),this}_handleClick(e){let t=this.nodeIdAt(e.target);t===null?this._selection.length>0&&(this.setSelection(null),this.emit(`selection`,this.getSelection())):(this.setSelection(t),this.emit(`selection`,this.getSelection()))}_handleDoubleClick(e){let t=this.nodeIdAt(e.target);if(t===null)return;let n=this._nodeData.get(t);n!==void 0&&this.emit(`activate`,n)}nodeIdAt(t){if(t===null)return null;let n=e.source.intern(t);for(let[t,r]of this._nodeComponents){let i=r.getElement();if(i&&(i===n||e.source.contains(i,n)))return t}return null}_handleWheel(t){t.preventDefault();let n=this.getZoom(),r=this.clampZoom(n*(t.deltaY<0?I:1/I));if(r===n)return;let i=e.source.getViewportRect(this),a=t.clientX-i.left,o=t.clientY-i.top,s=(a+this.getScrollLeft())/n,c=(o+this.getScrollTop())/n;this.setZoom(r),this.setScrollLeft(s*r-a),this.setScrollTop(c*r-o)}_handlePointerDown(e){e.button===0&&(this._panning=!0,this._panStartX=e.clientX,this._panStartY=e.clientY,this._panScrollLeft=this.getScrollLeft(),this._panScrollTop=this.getScrollTop())}_handlePointerMove(e){this._panning&&(this.setScrollLeft(this._panScrollLeft-(e.clientX-this._panStartX)),this.setScrollTop(this._panScrollTop-(e.clientY-this._panStartY)))}_handlePointerUp(){this._panning=!1}},V=r(DiagramView);export{M as DiagramEdgeLayer,x as DiagramGroupNode,v as DiagramNode,V as DiagramView,ElkLayoutEngine};
2
2
  //# sourceMappingURL=diagram.es.js.map