@evotecit/canopyx 0.3.16 → 0.3.18

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 (114) hide show
  1. package/CanopyX.Html/Assets/canopyx-board.css +50 -0
  2. package/CanopyX.Html/Assets/canopyx-board.js +39 -0
  3. package/CanopyX.Html/Assets/canopyx-buckets.js +15 -0
  4. package/CanopyX.Html/Assets/canopyx-calculations.css +7 -0
  5. package/CanopyX.Html/Assets/canopyx-calculations.js +52 -0
  6. package/CanopyX.Html/Assets/canopyx-calendar-edit.js +3 -3
  7. package/CanopyX.Html/Assets/canopyx-calendar.css +5 -24
  8. package/CanopyX.Html/Assets/canopyx-calendar.js +14 -19
  9. package/CanopyX.Html/Assets/canopyx-cell-range.js +16 -14
  10. package/CanopyX.Html/Assets/canopyx-changes.css +6 -24
  11. package/CanopyX.Html/Assets/canopyx-changes.js +18 -15
  12. package/CanopyX.Html/Assets/canopyx-compare.css +37 -0
  13. package/CanopyX.Html/Assets/canopyx-compare.js +12 -0
  14. package/CanopyX.Html/Assets/canopyx-data.js +3 -3
  15. package/CanopyX.Html/Assets/canopyx-editing.css +2 -2
  16. package/CanopyX.Html/Assets/canopyx-editing.js +2 -2
  17. package/CanopyX.Html/Assets/canopyx-enhance.js +4 -4
  18. package/CanopyX.Html/Assets/canopyx-export.js +54 -13
  19. package/CanopyX.Html/Assets/canopyx-facets.css +14 -5
  20. package/CanopyX.Html/Assets/canopyx-facets.js +19 -15
  21. package/CanopyX.Html/Assets/canopyx-filter-editor.css +71 -85
  22. package/CanopyX.Html/Assets/canopyx-filter-editor.js +20 -16
  23. package/CanopyX.Html/Assets/canopyx-flow-checks.css +1 -1
  24. package/CanopyX.Html/Assets/canopyx-flow-checks.js +1 -1
  25. package/CanopyX.Html/Assets/canopyx-flow-dialog.css +2 -2
  26. package/CanopyX.Html/Assets/canopyx-flow-dialog.js +1 -1
  27. package/CanopyX.Html/Assets/canopyx-flow-entry.css +136 -12
  28. package/CanopyX.Html/Assets/canopyx-flow-entry.js +2 -2
  29. package/CanopyX.Html/Assets/canopyx-flow-module.mjs +2 -2
  30. package/CanopyX.Html/Assets/canopyx-format-editor.js +2 -2
  31. package/CanopyX.Html/Assets/canopyx-formatting.js +10 -9
  32. package/CanopyX.Html/Assets/canopyx-grid-module.mjs +182 -128
  33. package/CanopyX.Html/Assets/canopyx-grid.css +228 -96
  34. package/CanopyX.Html/Assets/canopyx-grid.js +182 -128
  35. package/CanopyX.Html/Assets/canopyx-grouping.css +22 -15
  36. package/CanopyX.Html/Assets/canopyx-grouping.js +24 -17
  37. package/CanopyX.Html/Assets/canopyx-interactive.css +197 -74
  38. package/CanopyX.Html/Assets/canopyx-interactive.js +38 -45
  39. package/CanopyX.Html/Assets/canopyx-labels-de.js +177 -9
  40. package/CanopyX.Html/Assets/canopyx-labels-es.js +177 -9
  41. package/CanopyX.Html/Assets/canopyx-labels-fr.js +176 -8
  42. package/CanopyX.Html/Assets/canopyx-labels-it.js +176 -8
  43. package/CanopyX.Html/Assets/canopyx-labels-pl.js +176 -8
  44. package/CanopyX.Html/Assets/canopyx-layouts.css +48 -20
  45. package/CanopyX.Html/Assets/canopyx-layouts.js +36 -14
  46. package/CanopyX.Html/Assets/canopyx-link.js +2 -2
  47. package/CanopyX.Html/Assets/canopyx-live.css +1 -3
  48. package/CanopyX.Html/Assets/canopyx-live.js +3 -3
  49. package/CanopyX.Html/Assets/canopyx-matrix.css +1 -8
  50. package/CanopyX.Html/Assets/canopyx-matrix.js +11 -11
  51. package/CanopyX.Html/Assets/canopyx-module.mjs +38 -45
  52. package/CanopyX.Html/Assets/canopyx-office-export.js +2936 -0
  53. package/CanopyX.Html/Assets/canopyx-path.css +45 -0
  54. package/CanopyX.Html/Assets/canopyx-path.js +28 -0
  55. package/CanopyX.Html/Assets/canopyx-pivot.css +75 -0
  56. package/CanopyX.Html/Assets/canopyx-pivot.js +72 -0
  57. package/CanopyX.Html/Assets/canopyx-plan.css +56 -0
  58. package/CanopyX.Html/Assets/canopyx-plan.js +67 -0
  59. package/CanopyX.Html/Assets/canopyx-presenters.js +2 -2
  60. package/CanopyX.Html/Assets/canopyx-record-panel.css +8 -7
  61. package/CanopyX.Html/Assets/canopyx-record-panel.js +15 -14
  62. package/CanopyX.Html/Assets/canopyx-reporting-module.mjs +222 -161
  63. package/CanopyX.Html/Assets/canopyx-reporting.css +240 -107
  64. package/CanopyX.Html/Assets/canopyx-reporting.js +222 -161
  65. package/CanopyX.Html/Assets/canopyx-stack.css +35 -0
  66. package/CanopyX.Html/Assets/canopyx-stack.js +24 -0
  67. package/CanopyX.Html/Assets/canopyx-time-slider.css +18 -0
  68. package/CanopyX.Html/Assets/canopyx-time-slider.js +27 -0
  69. package/CanopyX.Html/Assets/canopyx-timeline.js +6 -5
  70. package/CanopyX.Html/Assets/canopyx-tree-tools.css +36 -35
  71. package/CanopyX.Html/Assets/canopyx-tree-tools.js +15 -12
  72. package/CanopyX.Html/Assets/canopyx-ui-menu.css +41 -0
  73. package/CanopyX.Html/Assets/canopyx-ui-menu.js +64 -0
  74. package/CanopyX.Html/Assets/canopyx-ui-navigation.css +73 -0
  75. package/CanopyX.Html/Assets/canopyx-ui-navigation.js +39 -0
  76. package/CanopyX.Html/Assets/canopyx-ui-overlays.css +79 -0
  77. package/CanopyX.Html/Assets/canopyx-ui-overlays.js +42 -0
  78. package/CanopyX.Html/Assets/canopyx-ui-picker.css +12 -0
  79. package/CanopyX.Html/Assets/canopyx-ui-picker.js +6 -0
  80. package/CanopyX.Html/Assets/canopyx-ui-stepper.css +174 -0
  81. package/CanopyX.Html/Assets/canopyx-ui-stepper.js +48 -0
  82. package/CanopyX.Html/Assets/canopyx-ui-tabs.css +153 -0
  83. package/CanopyX.Html/Assets/canopyx-ui-tabs.js +47 -0
  84. package/CanopyX.Html/Assets/canopyx-values.css +5 -0
  85. package/CanopyX.Html/Assets/canopyx-values.js +13 -0
  86. package/CanopyX.Html/Assets/canopyx-view-intents.css +12 -0
  87. package/CanopyX.Html/Assets/canopyx-view-intents.js +56 -0
  88. package/CanopyX.Html/Assets/canopyx.css +197 -71
  89. package/CanopyX.Html/Assets/canopyx.js +29 -19
  90. package/JavaScript/board.d.ts +30 -0
  91. package/JavaScript/changes.d.ts +4 -1
  92. package/JavaScript/facets.d.ts +5 -3
  93. package/JavaScript/grid.d.ts +150 -16
  94. package/JavaScript/hosts.d.ts +14 -1
  95. package/JavaScript/index.d.ts +4 -2
  96. package/JavaScript/locales/de.mjs +177 -9
  97. package/JavaScript/locales/es.mjs +177 -9
  98. package/JavaScript/locales/fr.mjs +176 -8
  99. package/JavaScript/locales/it.mjs +176 -8
  100. package/JavaScript/locales/pl.mjs +176 -8
  101. package/JavaScript/matrix.d.ts +4 -1
  102. package/JavaScript/path.d.ts +33 -0
  103. package/JavaScript/pivot.d.ts +135 -0
  104. package/JavaScript/plan.d.ts +23 -0
  105. package/JavaScript/protocol.d.ts +16 -3
  106. package/JavaScript/records.d.ts +2 -0
  107. package/JavaScript/stack.d.ts +28 -0
  108. package/JavaScript/time-slider.d.ts +58 -0
  109. package/JavaScript/ui.d.ts +261 -0
  110. package/JavaScript/view-intents.d.ts +45 -0
  111. package/README.md +22 -3
  112. package/ThirdParty/OfficeIMO/LICENSE +21 -0
  113. package/ThirdParty/OfficeIMO/README.md +13 -0
  114. package/package.json +76 -3
@@ -0,0 +1,35 @@
1
+ .cx-stack-host > .cx-grid{display:none;}
2
+ .cx-stack{min-width:0;min-height:0;flex:1 1 auto;overflow:auto;padding:var(--hfx-space-3,0.75rem);color:var(--hfx-color-text,light-dark(#16181c,#f2f3f5));font:var(--hfx-weight-regular,400) var(--hfx-type-body-size,0.875rem) var(--hfx-font-sans,"Segoe UI Variable Text","Segoe UI",system-ui,-apple-system,"Helvetica Neue",Arial,sans-serif);}
3
+ .cx-stack-title-heading{font:var(--hfx-weight-semibold,600) var(--hfx-type-heading-size,0.9375rem) var(--hfx-font-sans,"Segoe UI Variable Text","Segoe UI",system-ui,-apple-system,"Helvetica Neue",Arial,sans-serif);line-height:var(--hfx-type-heading-line,1.4);margin:0 0 var(--hfx-space-4,1rem);}
4
+ .cx-stack-winner{border:var(--hfx-border-width,1px) solid var(--hfx-color-status-good-fg,light-dark(#177345,#4cc78a));border-radius:var(--hfx-radius-container,var(--hfx-radius-lg,10px));background:var(--hfx-color-status-good-bg,light-dark(#e2f0e9,#1f362f));padding:calc(var(--hfx-space-1,0.25rem)*3.5) var(--hfx-space-4,1rem);}
5
+ .cx-stack-winner h3{color:var(--hfx-color-status-good-fg,light-dark(#177345,#4cc78a));font-size:var(--hfx-type-caption-size,0.75rem);margin:0 0 var(--hfx-space-1-5,6px);}
6
+ .cx-stack-title{height:auto;text-align:start;white-space:normal;overflow-wrap:anywhere;}
7
+ .cx-stack-title:hover{text-decoration:underline;}
8
+ .cx-stack-order{font-size:var(--hfx-type-body-size,0.875rem);margin:var(--hfx-space-5,1.5rem) 0 calc(var(--hfx-space-1,0.25rem)*2.5);}
9
+ .cx-stack-layers{margin:0;padding:0;list-style:none;display:flex;flex-direction:column;gap:calc(var(--hfx-space-1,0.25rem)*2.5);}
10
+ .cx-stack-layer{display:flex;gap:var(--hfx-space-3,0.75rem);padding:calc(var(--hfx-space-1,0.25rem)*3.5);border:var(--hfx-border-width,1px) solid var(--hfx-color-border,light-dark(#e2e4e7,#272a30));border-radius:var(--hfx-radius-container,var(--hfx-radius-lg,10px));background:var(--hfx-color-surface,light-dark(#ffffff,#16181c));break-inside:avoid;}
11
+ .cx-stack-number{display:grid;place-items:center;flex:0 0 calc(var(--hfx-space-1,0.25rem)*7);align-self:start;min-height:calc(var(--hfx-space-1,0.25rem)*7);border:var(--hfx-border-width,1px) solid var(--hfx-color-border,light-dark(#e2e4e7,#272a30));border-radius:50%;font-size:var(--hfx-type-caption-size,0.75rem);font-variant-numeric:tabular-nums;color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));}
12
+ .cx-stack-body{min-width:0;flex:1;}
13
+ .cx-stack-selected{border-color:var(--hfx-color-status-good-fg,light-dark(#177345,#4cc78a));}
14
+ .cx-stack-filtered{border-style:dashed;}
15
+ .cx-stack-reason{margin:var(--hfx-space-1-5,6px) 0;line-height:var(--hfx-type-body-line,1.5);overflow-wrap:anywhere;white-space:pre-wrap;}
16
+ .cx-stack-context,.cx-stack-empty{color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));margin:var(--hfx-space-1-5,6px) 0;font-size:var(--hfx-type-caption-size,0.75rem);overflow-wrap:anywhere;}
17
+ .cx-stack-badges{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hfx-space-1,0.25rem);margin-top:var(--hfx-space-2,0.5rem);}
18
+ .cx-stack-badges[hidden]{display:none;}
19
+ @media (max-width: 640px){
20
+ .cx-stack{padding:calc(var(--hfx-space-1,0.25rem)*2.5);}
21
+ .cx-stack-layer{padding:var(--hfx-space-3,0.75rem) calc(var(--hfx-space-1,0.25rem)*2.5);gap:calc(var(--hfx-space-1,0.25rem)*2.5);}
22
+ .cx-stack-title{min-height:calc(var(--hfx-space-1,0.25rem)*9);}
23
+ .cx-stack-winner{padding:var(--hfx-space-3,0.75rem);}
24
+ }
25
+ @media print{
26
+ .cx-stack-host,.cx-stack{height:auto !important;max-height:none !important;overflow:visible !important;}
27
+ .cx-stack-host > .cx-grid{display:none !important;}
28
+ .cx-stack-layers{display:block;}
29
+ .cx-stack-layer{margin-bottom:calc(var(--hfx-space-1,0.25rem)*2.5);}
30
+ .cx-stack-title{color:inherit;text-decoration:none;}
31
+ .cx-stack-winner,.cx-stack-layer{break-inside:avoid;}
32
+ }
33
+ .cx-stack .cx-stack-title{color:var(--hfx-color-text,light-dark(#16181c,#f2f3f5));font-weight:var(--hfx-weight-semibold,600);font-size:var(--hfx-type-body-size,0.875rem);line-height:var(--hfx-type-body-line,1.5);padding:0;border:0;border-radius:0;background:transparent;box-shadow:none;justify-content:flex-start;min-height:0;}
34
+ .cx-stack .cx-stack-title:hover{color:var(--hfx-color-text,light-dark(#16181c,#f2f3f5));background:transparent;text-decoration:underline;}
35
+ .cx-stack .cx-stack-title:focus-visible{outline:var(--hfx-focus-width,2px) solid var(--hfx-color-focus-ring,light-dark(#02726a,#37c9b8));outline-offset:var(--hfx-focus-offset,2px);}
@@ -0,0 +1,24 @@
1
+ (function(global){"use strict";const installed=new WeakSet();const part={version:"9ebd0cfb3f4f074b",install(context){if(installed.has(context.DataGrid.prototype))return;const{DataGrid,h,setText,fill,cellValue,cellText,badgeElement,applyTone,toneOf,PROTOCOL}=context;const STACK_TEXT={stackWinner:"Winner",stackEnforced:"Enforced",stackFiltered:"Filtered",stackEmpty:"No layers to display.",stackOrder:"Processing order",stackTruncated:"{shown} of {total} layers shown in processing order; the winning layer is always included."};const stackHasText=value=>typeof value==="string"&&/[^\u0009-\u000d\u0020\u0085\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000]/.test(value);
2
+ const stackObject=(value,keys)=>!!value&&typeof value==="object"&&!Array.isArray(value)&&Object.keys(value).every(key=>keys.includes(key));function stackIdentifier(value){if(!stackHasText(value))return false;for(let index=0;index<value.length;index++){const code=value.charCodeAt(index);if(code>=0xd800&&code<=0xdbff){const next=value.charCodeAt(++index);if(!(next>=0xdc00&&next<=0xdfff))return false}
3
+ else if(code>=0xdc00&&code<=0xdfff)return false}
4
+ return true}
5
+ function stackSettings(grid){grid.validateStackSource();const input=grid.view.stack,known=id=>grid.columns.some(column=>column.id===id);if(!stackObject(input,["titleColumn","layers"])||
6
+ input.titleColumn!==undefined&&(!stackIdentifier(input.titleColumn)||!known(input.titleColumn))||
7
+ !Array.isArray(input.layers)||input.layers.length>1000)
8
+ throw new Error("CanopyX: a stack needs inline records, stable identities, known title columns and at most 1,000 layers.");const titleColumn=input.titleColumn||grid.columns.find(column=>column.rowHeader)?.id||grid.columns.find(column=>!column.hidden)?.id;if(!titleColumn)throw new Error("CanopyX: a stack needs a title column.");const records=new Map((grid.view.rows||[]).map(row=>[row.id,row])),ids=new Set();let winners=0;for(const layer of input.layers){if(!stackObject(layer,["id","record","reason","label","winner","enforced","filtered","badges"])||
9
+ !stackIdentifier(layer.id)||!stackIdentifier(layer.record)||ids.has(layer.id)||!records.has(layer.record)||
10
+ !stackHasText(layer.reason)||layer.label!==undefined&&typeof layer.label!=="string"||
11
+ ["winner","enforced","filtered"].some(name=>layer[name]!==undefined&&typeof layer[name]!=="boolean")||
12
+ layer.badges!==undefined&&(!Array.isArray(layer.badges)||layer.badges.some(badge=>!stackObject(badge,["text","tone","title"])||typeof badge.text!=="string"||!badge.text||
13
+ badge.tone!==undefined&&(typeof badge.tone!=="string"||!/^[a-z][a-z0-9-]{0,31}$/.test(badge.tone) || badge.tone.endsWith("-bg")) || badge.title !== undefined && typeof badge.title !== "string")))
14
+ throw new Error("CanopyX: stack layers need unique ids, existing records, reasons and valid badges.");ids.add(layer.id);if(layer.winner)winners++;if(layer.winner&&layer.filtered)throw new Error("CanopyX: a filtered stack layer cannot be the winner.")}
15
+ if(input.layers.length&&winners!==1)throw new Error("CanopyX: a nonempty stack needs exactly one host-supplied winner.");return{...input,titleColumn,records}}
16
+ Object.assign(DataGrid.prototype,{validateStackSource(){if(this.view.source.kind!=="inline"||this.opts.source||this.view.rowIdentity==="ordinal")
17
+ throw new Error("CanopyX: a stack requires stable inline records.")},startStack(){if(this.stack)return;const settings=stackSettings(this),labels={};for(const[key,text]of Object.entries(STACK_TEXT))labels[key]=this.opts.labels?.[key]||this.view.labels?.[key]||text;this.stack={settings,labels};const previousHidden=this.grid.hidden,pagerHidden=this.pager?.hidden;const toolbarHidden=this.toolbar.hidden,originalMaxHeight=this.fitInline??this.host.style.maxHeight;this.host.style.maxHeight=originalMaxHeight;this.toolbar.hidden=true;this.host.classList.add("cx-stack-host");this.grid.hidden=true;if(this.pager)this.pager.hidden=true;this.stackFrame=h("section","cx-stack");this.stackFrame.setAttribute("aria-label",this.view.label);this.host.insertBefore(this.stackFrame,this.grid.nextSibling);const repaint=()=>{try{this.stack.settings=stackSettings(this);if(this.stack.failure){this.stack.failure=false;this.setNotice("",false)}
18
+ this.stackPaint()}catch(error){this.stack.settings={...this.stack.settings,records:new Map()};this.stack.failure=true;this.stackFrame.replaceChildren();this.setNotice(error.message,true);this.emit("error",{error})}};this.host.addEventListener("canopyx:format",repaint,{signal:this.lifetime.signal});this.host.addEventListener("canopyx:rowsChanged",repaint,{signal:this.lifetime.signal});this.lifetime.signal.addEventListener("abort",()=>{this.stackFrame.remove();this.host.classList.remove("cx-stack-host");this.grid.hidden=previousHidden;this.toolbar.hidden=toolbarHidden;this.host.style.maxHeight=originalMaxHeight;if(this.pager)this.pager.hidden=pagerHidden},{once:true});this.stackPaint()}});function stackRecordTemplate(text){const button=h("button","cx-btn cx-stack-title");button.type="button";setText(button,text);return button}
19
+ function stackBadgesTemplate(layer,labels){const badges=h("div","cx-stack-badges");if(layer.winner)badges.appendChild(badgeElement({text:labels.stackWinner,tone:"success"}));if(layer.enforced)badges.appendChild(badgeElement({text:labels.stackEnforced,tone:"info"}));if(layer.filtered)badges.appendChild(badgeElement({text:labels.stackFiltered,tone:"neutral"}));for(const badge of layer.badges||[])badges.appendChild(badgeElement(badge));badges.hidden=!badges.childElementCount;return badges}
20
+ function stackFrameTemplate(stack,frame,layers,recordButton,attachLayer){const stackBadges=layer=>stackBadgesTemplate(layer,stack.labels);const viewTitle=h("h2","cx-stack-title-heading");setText(viewTitle,this.view.label);frame.appendChild(viewTitle);if(!layers.length){const empty=h("p","cx-stack-empty");setText(empty,stack.labels.stackEmpty);frame.appendChild(empty);return null}
21
+ const winner=layers.find(layer=>layer.winner),summary=h("header","cx-stack-winner"),summaryLabel=h("h3"),summaryTitle=recordButton(winner);applyTone(summary,"cx-stack-winner",toneOf("success"));setText(summaryLabel,stack.labels.stackWinner);summary.append(summaryLabel,summaryTitle);if(winner.label){const label=h("p","cx-stack-context");setText(label,winner.label);summary.appendChild(label)}
22
+ const summaryReason=h("p","cx-stack-reason");setText(summaryReason,winner.reason);summary.append(summaryReason,stackBadges(winner));frame.appendChild(summary);const heading=h("h3","cx-stack-order"),list=h("ol","cx-stack-layers");heading.id=this.id+"-stack-order";setText(heading,stack.labels.stackOrder);list.setAttribute("aria-labelledby",heading.id);list.setAttribute("role","list");frame.append(heading,list);layers.forEach((layer,index)=>{const item=h("li","cx-stack-layer"),number=h("span","cx-stack-number"),body=h("div","cx-stack-body");item.dataset.cxStackLayer=layer.id;item.dataset.cxStackRecord=layer.record;item.classList.toggle("cx-stack-filtered",!!layer.filtered);item.classList.toggle("cx-stack-selected",!!layer.winner);number.setAttribute("aria-hidden","true");setText(number,String(index+1));const title=recordButton(layer),reason=h("p","cx-stack-reason");reason.id=this.id+"-stack-reason-"+index;title.setAttribute("aria-describedby",reason.id);setText(reason,layer.reason);body.appendChild(title);if(layer.label){const label=h("p","cx-stack-context");setText(label,layer.label);body.appendChild(label)}
23
+ body.append(reason,stackBadges(layer));item.append(number,body);list.appendChild(item);attachLayer(title,list,index)});return list}
24
+ Object.assign(DataGrid.prototype,{stackTitle(layer){const stack=this.stack,row=stack.settings.records.get(layer.record),column=this.columns.find(column=>column.id===stack.settings.titleColumn);return cellText(row.cells[column.id],column,this.fmt)||row.id},stackRecordButton(layer){const button=stackRecordTemplate(this.stackTitle(layer));button.addEventListener("click",()=>this.openRecord(layer.record,{returnFocus:button}).catch(error=>{this.setNotice(error.message,true);this.emit("error",{error})}));return button},stackPaint(){const stack=this.stack,frame=this.stackFrame,layers=stack.settings.layers;const held=document.activeElement,focusLayer=frame.contains(held)?held.closest("[data-cx-stack-layer]")?.dataset.cxStackLayer:null;frame.replaceChildren();const list=stackFrameTemplate.call(this,stack,frame,layers,layer=>this.stackRecordButton(layer),(title,list,index)=>{title.addEventListener("keydown",event=>{if(event.altKey||event.ctrlKey||event.metaKey||event.shiftKey)return;const next=event.key==="Home"?0:event.key==="End"?layers.length-1:event.key==="ArrowDown"?Math.min(layers.length-1,index+1):event.key==="ArrowUp"?Math.max(0,index-1):null;if(next!==null){event.preventDefault();list.children[next].querySelector("button").focus()}})});if(!list)return;if(focusLayer)[...list.children].find(item=>item.dataset.cxStackLayer===focusLayer)?.querySelector("button").focus({preventScroll:true})}});installed.add(context.DataGrid.prototype)}};(global.CanopyXParts||=[]).push(["stack",part])})(typeof window!=="undefined"?window:globalThis);
@@ -0,0 +1,18 @@
1
+ .cx-time-host{display:flex;flex-direction:column;min-height:0;box-sizing:border-box;border:var(--hfx-border-width,1px) solid var(--hfx-color-border,light-dark(#e2e4e7,#272a30));border-radius:var(--hfx-radius-container,var(--hfx-radius-lg,10px));background:var(--hfx-color-surface,light-dark(#ffffff,#16181c));color:var(--hfx-color-text,light-dark(#16181c,#f2f3f5));overflow:auto;}
2
+ .cx-time-host > .cx-matrix{flex:1 1 auto;min-height:0;max-height:none;border:0;border-radius:0;background:inherit;}
3
+ .cx-time{flex:none;font:var(--hfx-weight-regular,400) var(--hfx-type-body-size,0.875rem) var(--hfx-font-sans,"Segoe UI Variable Text","Segoe UI",system-ui,-apple-system,"Helvetica Neue",Arial,sans-serif);display:grid;gap:var(--hfx-space-2,0.5rem);padding:var(--hfx-space-4,1rem) 0;color:var(--hfx-color-text,light-dark(#16181c,#f2f3f5));min-inline-size:0;background:inherit;}
4
+ .cx-time-controls{display:flex;align-items:center;gap:var(--hfx-space-2,0.5rem);min-inline-size:0;}
5
+ .cx-time-button{flex:none;min-block-size:var(--hfx-control-height-lg,calc(44px - var(--cx-compact,0) * 8px));min-inline-size:var(--hfx-control-height-lg,calc(44px - var(--cx-compact,0) * 8px));}
6
+ .cx-time-button svg{inline-size:var(--hfx-icon-lg,20px);block-size:var(--hfx-icon-lg,20px);fill:none;stroke:currentColor;stroke-width:2;stroke-linejoin:round;}
7
+ .cx-time[data-playing="true"] .cx-time-button[aria-pressed="true"]{color:var(--hfx-color-accent-contrast,light-dark(#ffffff,#0c0d10));background:var(--hfx-color-accent,light-dark(#11776f,#37c9b8));border-color:var(--hfx-color-accent,light-dark(#11776f,#37c9b8));}
8
+ .cx-time-scrubber{position:relative;min-inline-size:0;padding-inline:var(--hfx-space-2,0.5rem);}
9
+ .cx-time-range{display:block;box-sizing:border-box;inline-size:100%;margin:0;accent-color:var(--hfx-color-accent,light-dark(#11776f,#37c9b8));block-size:var(--hfx-control-height-sm,calc(28px - var(--cx-compact,0) * 4px));}
10
+ .cx-time-range:focus-visible{outline:var(--hfx-focus-width,2px) solid var(--hfx-color-focus-ring,light-dark(#02726a,#37c9b8));outline-offset:var(--hfx-focus-offset,2px);}
11
+ .cx-time-ticks{position:relative;block-size:var(--hfx-space-2,0.5rem);margin-inline:var(--hfx-space-2,0.5rem);}
12
+ .cx-time-ticks i{position:absolute;inline-size:var(--hfx-border-width,1px);block-size:var(--hfx-space-1-5,6px);background:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));}
13
+ .cx-time-position{color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));font-variant-numeric:tabular-nums;font-size:var(--hfx-type-caption-size,0.75rem);}
14
+ .cx-time-stamp{font-weight:var(--hfx-weight-semibold,600);line-height:var(--hfx-type-body-line,1.5);margin-inline-start:var(--hfx-space-2,0.5rem);overflow-wrap:anywhere;}
15
+ .cx-time-reduced{margin:0;color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));font-size:var(--hfx-type-caption-size,0.75rem);}
16
+ .cx-time [hidden]{display:none;}
17
+ @media(max-width:480px){.cx-time{padding:var(--hfx-space-3,0.75rem) 0;}.cx-time-controls{flex-wrap:wrap;}.cx-time-stamp{flex-basis:100%;margin-inline-start:0;}}
18
+ @media print{.cx-time-host{border:0;}.cx-time{padding:0;}.cx-time-controls .cx-time-button,.cx-time-scrubber,.cx-time-reduced{display:none;}.cx-time-stamp{display:block;color:inherit;}}
@@ -0,0 +1,27 @@
1
+ (function(global){"use strict";const installed=new WeakSet();const part={version:"a4888d801922c858",install(context){if(installed.has(context.TIME_SLIDER_IMPL))return;const{TIME_SLIDER_IMPL,h,setText,fill,PROTOCOL}=context;function humanTimeFormat(lang,local,utcTemplate){const formatter=new Intl.DateTimeFormat(lang||undefined,{year:"numeric",month:"short",day:"numeric",hour:"2-digit",minute:"2-digit",hourCycle:"h23",timeZone:local?undefined:"UTC",...(local?{timeZoneName:"short"}:{})});return{format(value){const parts=formatter.formatToParts(new Date(value));const text=parts.map((part,index)=>part.type==="literal"&&parts[index+1]?.type==="timeZoneName"?part.value.replace(/\s/g,"\u00a0"):part.value).join("");
2
+ return local?text:fill(utcTemplate||"{time} UTC",{time:text}).replace(/ (\S+)$/,"\u00a0$1");
3
+ }}}
4
+ const TIME_TEXT={timePlay:"Play",timePause:"Pause",timePrevious:"Previous snapshot",timeNext:"Next snapshot",timePosition:"Snapshot {index} of {count}",timeFrame:"Snapshot at {time}",timeReducedMotion:"Timed playback is off because reduced motion is enabled. Step through snapshots manually."};function timeFail(message){throw new Error("CanopyX: the time slider is not valid: "+message+".")}
5
+ function timeKeys(value,names,what){if(!value||typeof value!=="object"||Array.isArray(value))timeFail(what+" must be an object");for(const name of Object.keys(value))if(!names.includes(name))timeFail(what+" has the unknown property '"+name+"'")}
6
+ function timeModel(source,kind,validate){const input=source.timeSlider;timeKeys(input,["interval","initial","snapshots"],"settings");const interval=input.interval===undefined?1000:input.interval;if(!Number.isInteger(interval)||interval<100||interval>60000)timeFail("interval must be 100 to 60000 milliseconds");if(!Array.isArray(input.snapshots)||!input.snapshots.length||input.snapshots.length>500)timeFail("supply 1 to 500 snapshots");let previous=-Infinity;const snapshots=input.snapshots.map(item=>{timeKeys(item,["at","label","view"],"snapshot");if(typeof item.at!=="string"||!/^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(?:\.\d{1,7})?(?:Z|[+-]\d\d:\d\d)$/.test(item.at)) timeFail("snapshot time needs an ISO timestamp with a zone");
7
+ const parts=item.at.match(/\d+/g).map(Number), [year, month, day, hour, minute, second] = parts;
8
+ const zone=item.at.match(/([+-])(\d\d):(\d\d)$/);
9
+ const days=month===2?year%4===0&&(year%100!==0||year%400===0)?29:28:[4,6,9,11].includes(month)?30:31;if(!year||month<1||month>12||day<1||day>days||hour>23||minute>59||second>59||
10
+ zone&&(+zone[2]>14||+zone[3]>59||+zone[2]===14&&+zone[3]!==0))timeFail("snapshot time is outside the supported date range");const instant=Date.parse(item.at);if(!Number.isFinite(instant)||instant<=previous)timeFail("snapshot times must increase strictly");previous=instant;if(item.label!==undefined&&(typeof item.label!=="string"||!/\S/.test(item.label) || item.label.length > 200)) timeFail("snapshot label must be visible text of at most 200 characters");
11
+ if(!item.view||item.view.type!==kind||item.view.id!==source.id||Object.hasOwn(item.view,"timeSlider"))timeFail("snapshots need the same view type and id, without nested time sliders");validate(item.view);const view=JSON.parse(JSON.stringify(item.view));return Object.freeze({at:item.at,label:item.label,view})});const initial=input.initial===undefined?snapshots.length-1:input.initial;if(!Number.isInteger(initial)||initial<0||initial>=snapshots.length)timeFail("initial must identify a supplied snapshot");return{interval,initial,snapshots}}
12
+ function timeLabels(...sources){const labels={...TIME_TEXT};for(const source of sources)for(const[key,value]of Object.entries(source||{})){if(!Object.hasOwn(labels,key)){if(/^time[A-Z]/.test(key)) timeFail("unknown label '" + key + "'"); continue; }
13
+ if(typeof value!=="string"||!/\S/.test(value)) timeFail("label '" + key + "' needs visible text");
14
+ labels[key]=value}
15
+ return labels}
16
+ function timeIcon(button,playing,direction){const svg=document.createElementNS("http://www.w3.org/2000/svg","svg"),path=document.createElementNS(svg.namespaceURI,"path");svg.setAttribute("viewBox","0 0 24 24");svg.setAttribute("aria-hidden","true");path.setAttribute("d",direction===-1?"M7 5V19M18 5L9 12L18 19Z":direction===1?"M17 5V19M6 5L15 12L6 19Z":playing?"M8 5V19M16 5V19":"M7 4L20 12L7 20Z");svg.appendChild(path);button.replaceChildren(svg)}
17
+ function timeControlsTemplate(model,labels,source,hostLabels,hostLang){const root=h("section","cx-time"),controls=h("div","cx-time-controls"),range=h("input","cx-time-range");const stamp=h("div","cx-time-stamp"),position=h("span","cx-time-position"),reduced=h("p","cx-time-reduced");const scrubber=h("div","cx-time-scrubber"),ticks=h("div","cx-time-ticks");ticks.setAttribute("aria-hidden","true");const formatter=humanTimeFormat(source.lang||hostLang||document.documentElement.lang,source.timeZone==="local",hostLabels?.changeUtcTime??source.labels?.changeUtcTime);const button=text=>{const result=h("button","cx-btn icon cx-time-button");result.type="button";result.setAttribute("aria-label",text);result.title=text;return result};const previous=button(labels.timePrevious),play=button(labels.timePlay),next=button(labels.timeNext);timeIcon(previous,false,-1);timeIcon(next,false,1);range.type="range";range.min="0";range.max=String(model.snapshots.length-1);range.step="1";range.setAttribute("aria-label",labels.timePosition.replace("{index}","").replace("{count}",String(model.snapshots.length)).trim());stamp.setAttribute("aria-live","polite");root.setAttribute("aria-label",labels.timeFrame.replace("{time}","").trim());setText(reduced,labels.timeReducedMotion);for(const[index,item]of model.snapshots.entries()){const tick=h("i");tick.style.insetInlineStart=(model.snapshots.length===1?0:index*100 / (model.snapshots.length - 1)) + "%";
18
+ tick.title=formatter.format(new Date(item.at));ticks.appendChild(tick)}
19
+ controls.append(previous,play,next,stamp);scrubber.append(range,ticks);root.append(controls,scrubber,position,reduced);return{root,range,stamp,position,reduced,previous,play,next,formatter}}
20
+ function timeStateTemplate(elements,model,labels,index,playing,reducedMotion){const{root,range,stamp,position,reduced,previous,play,next,formatter}=elements;const item=model.snapshots[index],text=fill(labels.timePosition,{index:index+1,count:model.snapshots.length});const time=formatter.format(new Date(item.at));range.value=String(index);range.setAttribute("aria-valuetext",text+": "+time);setText(position,text);setText(stamp,(item.label?item.label+" · ":"")+ fill(labels.timeFrame,{time}));previous.disabled=index===0;next.disabled=index===model.snapshots.length-1;play.disabled=reducedMotion||model.snapshots.length<2||!playing&&index===model.snapshots.length-1;root.dataset.playing=String(playing);play.setAttribute("aria-pressed",String(playing));const name=playing?labels.timePause:labels.timePlay;play.setAttribute("aria-label",name);play.title=name;timeIcon(play,playing);reduced.hidden=!reducedMotion}
21
+ function timeCreate(host,source,hostLabels,kind,validate,update){const model=timeModel(source,kind,validate),labels=timeLabels(source.labels,hostLabels);const lifetime=new AbortController(),signal=lifetime.signal,media=matchMedia("(prefers-reduced-motion: reduce)");const elements=timeControlsTemplate(model,labels,source,hostLabels,host.closest("[lang]")?.getAttribute("lang"));const{root,range,previous,play,next}=elements;const hadFrame=host.classList.contains("cx-time-host");host.classList.add("cx-time-host");let index=model.initial,playing=false,timer=null,disposed=false;const state=()=>Object.freeze({index,count:model.snapshots.length,at:model.snapshots[index].at,playing,reducedMotion:media.matches});const paint=()=>timeStateTemplate(elements,model,labels,index,playing,media.matches);const pause=()=>{playing=false;clearTimeout(timer);timer=null;if(!disposed)paint();return state()};const seek=target=>{if(disposed)throw new DOMException("Snapshot playback was destroyed.","AbortError");if(!Number.isInteger(target)||target<0||target>=model.snapshots.length)throw new RangeError("Snapshot index is outside the supplied history.");pause();update(model.snapshots[target].view);index=target;paint();return state()};const tick=()=>{timer=setTimeout(()=>{if(disposed||!playing)return;if(media.matches){pause();return}
22
+ try{update(model.snapshots[index+1].view);++index}
23
+ catch(error){pause();host.dispatchEvent(new CustomEvent("canopyx:error",{detail:{error},bubbles:true}));return}
24
+ if(index===model.snapshots.length-1)pause();else{paint();tick()}},model.interval)};const start=()=>{if(!disposed&&!playing&&!media.matches&&index<model.snapshots.length-1){playing=true;paint();tick()}return state()};previous.addEventListener("click",()=>seek(index-1),{signal});next.addEventListener("click",()=>seek(index+1),{signal});play.addEventListener("click",()=>playing?pause():start(),{signal});range.addEventListener("input",()=>seek(Number(range.value)),{signal});media.addEventListener("change",pause,{signal});window.addEventListener("beforeprint",pause,{signal});try{update(model.snapshots[index].view);paint();host.prepend(root)}
25
+ catch(error){lifetime.abort();root.remove();if(!hadFrame)host.classList.remove("cx-time-host");throw error}
26
+ return{play:start,pause,seek,step:(delta=1)=>{if(!Number.isInteger(delta))throw new RangeError("Snapshot step must be an integer.");return seek(Math.max(0,Math.min(model.snapshots.length-1,index+delta)))},getState:state,destroy:()=>{if(disposed)return;pause();disposed=true;lifetime.abort();root.remove();if(!hadFrame)host.classList.remove("cx-time-host")}}}
27
+ TIME_SLIDER_IMPL.create=timeCreate;installed.add(context.TIME_SLIDER_IMPL)}};(global.CanopyXParts||=[]).push(["time-slider",part])})(typeof window!=="undefined"?window:globalThis);
@@ -1,4 +1,4 @@
1
- (function(global){"use strict";const installed=new WeakSet();const part={version:"22c9e15c108aeaa1",requires:["calendar"],install(context){if(installed.has(context.DataGrid.prototype))return;const{toneVariable,DataGrid,TreeGrid,h,setText,fill,numberFormat}=context;const{CAL_DAY,CAL_HOUR,CAL_MINUTE,calAddMonths,calCount,calDayNumber,calDayParts,calDow,calMonthStart,calWeekStart}=DataGrid.prototype.calKit;const TL_ZOOMS={hour:{width:56,unit:"hour"},day:{width:44,unit:"day"},week:{width:92,unit:"week"},month:{width:112,unit:"month"},quarter:{width:150,unit:"quarter"}};function tlSpan(zoom,date,firstDay){if(zoom==="hour")return{firstDay:date,lastDay:date};if(zoom==="day"){const month=calMonthStart(date);return{firstDay:month,lastDay:calAddMonths(month,1)- 1}}
1
+ (function(global){"use strict";const installed=new WeakSet();const part={version:"ece982b3b7277183",requires:["calendar"],install(context){if(installed.has(context.DataGrid.prototype))return;const{toneVariable,DataGrid,TreeGrid,h,setText,fill,numberFormat}=context;const{CAL_DAY,CAL_HOUR,CAL_MINUTE,calAddMonths,calCount,calDayNumber,calDayParts,calDow,calMonthStart,calWeekStart}=DataGrid.prototype.calKit;const TL_ZOOMS={hour:{width:56,unit:"hour"},day:{width:44,unit:"day"},week:{width:92,unit:"week"},month:{width:112,unit:"month"},quarter:{width:150,unit:"quarter"}};function tlSpan(zoom,date,firstDay){if(zoom==="hour")return{firstDay:date,lastDay:date};if(zoom==="day"){const month=calMonthStart(date);return{firstDay:month,lastDay:calAddMonths(month,1)- 1}}
2
2
  if(zoom==="week"){const parts=calDayParts(date),quarter=calDayNumber(parts.y,Math.floor((parts.m-1)/ 3) * 3 + 1, 1);
3
3
  return{firstDay:calWeekStart(quarter,firstDay),lastDay:calWeekStart(calAddMonths(quarter,3)- 1,firstDay)+ 6}}
4
4
  const parts=calDayParts(date);if(zoom==="month")return{firstDay:calDayNumber(parts.y,1,1),lastDay:calDayNumber(parts.y+1,1,1)- 1};return{firstDay:calDayNumber(parts.y,1,1),lastDay:calDayNumber(parts.y+3,1,1)- 1}}
@@ -8,7 +8,8 @@ function tlCustomStep(span,date,direction){return span.unit==="month"?calAddMont
8
8
  function tlStep(zoom,date,direction){if(zoom==="hour")return date+direction;if(zoom==="day")return calAddMonths(date,direction);if(zoom==="week")return calAddMonths(date,3*direction);return calAddMonths(date,(zoom==="month"?12:36)*direction)}
9
9
  function tlUnits(zoom,span,zone,firstDay){const units=[],width=TL_ZOOMS[zoom].width;const push=(first,last,start,end)=>units.push({first,last,start,end,x:units.length*width});if(zoom==="hour"){for(let at=zone.start(span.firstDay),end=zone.start(span.lastDay+1);at<end;at +=CAL_HOUR){const day=zone.day(at);push(day,day,at,Math.min(at+CAL_HOUR,end))}}else if(zoom==="day"){for(let day=span.firstDay;day<=span.lastDay;day++)push(day,day,zone.start(day),zone.start(day+1))}else if(zoom==="week"){for(let day=calWeekStart(span.firstDay,firstDay);day<=span.lastDay;day +=7)push(day,day+6,zone.start(day),zone.start(day+7))}else{const months=zoom==="month"?1:3;for(let day=span.firstDay;day<=span.lastDay;day=calAddMonths(day,months)){const next=calAddMonths(day,months);push(day,next-1,zone.start(day),zone.start(next))}}
10
10
  return units}
11
- function tlAxis(zoom,span,zone,firstDay){const units=tlUnits(zoom,span,zone,firstDay),width=TL_ZOOMS[zoom].width,total=units.length*width;const unitOf=time=>{let low=0,high=units.length-1;while(low<high){const mid=(low+high+1)>>1;if(units[mid].start<=time)low=mid;else high=mid-1}
11
+ function tlAxis(zoom,span,zone,firstDay,minWidth=0){const units=tlUnits(zoom,span,zone,firstDay),width=Math.max(TL_ZOOMS[zoom].width,minWidth / units.length), total = units.length * width;
12
+ units.forEach((unit,index)=>{unit.x=index*width});const unitOf=time=>{let low=0,high=units.length-1;while(low<high){const mid=(low+high+1)>>1;if(units[mid].start<=time)low=mid;else high=mid-1}
12
13
  return low};return{zoom,units,width,total,from:units[0].start,to:units[units.length-1].end,x(time){if(time<=this.from)return 0;if(time>=this.to)return total;const unit=units[unitOf(time)];return unit.x +(time-unit.start)/ (unit.end - unit.start) * width;
13
14
  },time(x){const index=Math.max(0,Math.min(units.length-1,Math.floor(x / width))), unit = units[index];
14
15
  return unit.start+Math.max(0,Math.min(1,(x-unit.x)/ width)) * (unit.end - unit.start);
@@ -24,11 +25,11 @@ Object.assign(DataGrid.prototype,{startTimeline(){if(this.tl)return;const resour
24
25
  const inline=!resources.view.source||resources.view.source.kind==="inline";if(inline)this.tlLearnNodes(resources.view.roots||[],null);this.tlInline=inline},tlLearnNodes(nodes,parent){const stack=nodes.map(node=>[node,parent]).reverse();while(stack.length){const[node,owner]=stack.pop();if(!node||typeof node.id!=="string"||this.tl.learned.has(node.id))continue;this.tl.learned.add(node.id);this.tl.names.set(node.id,node.label??node.id);this.tl.parent.set(node.id,owner);this.tl.order.push(node.id);if(owner!==null){if(!this.tl.children.has(owner))this.tl.children.set(owner,[]);this.tl.children.get(owner).push(node.id)}
25
26
  if(Array.isArray(node.children))for(let i=node.children.length-1;i>=0;i--)stack.push([node.children[i],node.id])}},calResourceName(id){return this.tl?.names.get(id)??id},calResourceColumns(){if(!this.tl||!this.tlInline)return null;const leaves=this.tl.order.filter(id=>!this.tl.children.has(id));if(!leaves.length)return null;return leaves.map(id=>({id,label:this.calResourceName(id),group:this.tl.parent.get(id)==null?null:this.calResourceName(this.tl.parent.get(id))}))},calHoursFor(resource,dow){const list=this.cal.availability;if(!list.length)return undefined;let own=resource==null?[]:list.filter(item=>item.resource===resource);for(let parent=this.tl?.parent.get(resource);!own.length&&parent!=null;parent=this.tl.parent.get(parent))own=list.filter(item=>item.resource===parent);const entries=own.length?own:list.filter(item=>item.resource==null);return entries.length?entries.filter(item=>item.days.has(dow)):undefined},calResourceStep(resource,sign){if(this.tlSolo)return resource;const rows=this.tlTree?this.tlTree.rows.filter(row=>!row.status&&!this.tl.children.has(row.key)).map(row=>row.key):this.tl.order.filter(id=>!this.tl.children.has(id));const at=rows.indexOf(resource);return rows[Math.max(0,Math.min(rows.length-1,(at<0?0:at)+ sign))]??resource},calResourceWindow(){if(this.cal.resourceIds||this.calView!=="timeline"||this.tlSolo||this.view.source.kind==="inline"&&!this.opts.source)return null;this.tlEnsure();if(!this.tlTree?.rows?.length)return null;const range=this.tlMain.clientHeight?this.tlVisibleRows(40):{first:0,last:Math.min(this.tlTree.rows.length-1,80)},ids=new Set();for(let i=range.first;i<=range.last&&ids.size<1000;i++){const row=this.tlTree.rows[i];if(!row||row.status)continue;const stack=[row.key];while(stack.length&&ids.size<1000){const id=stack.pop();if(ids.has(id))continue;ids.add(id);const children=this.tl.children.get(id)||[];for(let at=children.length-1;at>=0;at--)stack.push(children[at])}}
26
27
  const window=[...ids];return window.length?window:null},tlCheckWindow(){if(this.view.source.kind==="inline"&&!this.opts.source)return;clearTimeout(this.tlWindowTimer);this.tlWindowTimer=setTimeout(()=>{if(this.destroyed||this.calView!=="timeline")return;const wanted=this.calResourceWindow(),loaded=JSON.parse(this.calWindowKey||"null"),known=new Set(loaded||[]);if(wanted&&(!loaded||wanted.some(id=>!known.has(id))))this.calReload({keep:true})},250)},calResourcesReady(){if(this.calView!=="timeline"||this.view.source.kind==="inline"&&!this.opts.source)return null;this.tlEnsure();return this.tlTree.ready?.catch(()=>{})},calTimelineRange(date){return this.cal.span?tlCustomSpan(this.cal.span,date,this.cal.firstDay):tlSpan(this.calZoom,date,this.cal.firstDay)},calTimelineStep(date,direction){return this.cal.span?tlCustomStep(this.cal.span,date,direction):tlStep(this.calZoom,date,direction)},calTimelineTitle(){const range=this.calRange,fmt=this.calFmt;if(this.cal.span)return range.firstDay===range.lastDay?fmt.longDay(range.firstDay):fmt.dayRange(range.firstDay,range.lastDay);if(this.calZoom==="hour")return fmt.longDay(range.firstDay);if(this.calZoom==="day")return fmt.monthTitle(range.firstDay);if(this.calZoom==="week")return fmt.monthRange(range.firstDay,range.lastDay);return range.lastDay-range.firstDay>400?fmt.year(range.firstDay)+ " – "+fmt.year(range.lastDay):fmt.year(range.firstDay)},calTimelineHasNow(){const now=Date.now();return this.tlAxis?now>=this.tlAxis.from&&now<this.tlAxis.to:false},tlEnsure(){if(this.tlRoot)return;const resources=this.cal.resources,root=this.tlRoot=h("div","cx-tl" +(this.tlSolo?" cx-tl-solo":"")),side=h("div","cx-tl-side"),host=this.tlTreeHost=h("div","cx-tl-tree");const main=this.tlMain=h("div","cx-tl-main"),head=this.tlHead=h("div","cx-tl-head"),canvas=this.tlCanvas=h("div","cx-tl-canvas");head.setAttribute("aria-hidden","true");const upper=this.tlUpper=h("div","cx-tl-hrow cx-tl-upper"),lower=this.tlLower=h("div","cx-tl-hrow cx-tl-lower");head.append(upper,lower);canvas.setAttribute("role","grid");canvas.setAttribute("aria-labelledby",this.calTitle.id);canvas.setAttribute("aria-readonly",String(!this.cal.editable));if(this.tlSolo){root.append(main);main.append(head,canvas);const signal=this.lifetime.signal;this.tlTree={rows:[],tops:[0],rowH:this.rowH||34,version:0,destroyed:false,ready:Promise.resolve(),grid:side,scheduleRender:()=>this.tlScheduleLanes(),setState:()=>Promise.resolve(),destroy(){this.destroyed=true}};main.addEventListener("scroll",()=>this.tlScheduleLanes(),{passive:true,signal});return}
27
- root.style.setProperty("--cx-tl-side",resources.width+"px");main.append(head,canvas);side.appendChild(host);root.append(side,main);const aggregates=resources.aggregates,renderers={};for(const[column,kind]of Object.entries(aggregates))renderers[column]=(node,element)=>{setText(element,this.tlAggregateText(node.id,kind));element.classList.add("cx-tl-agg");return true};const view={...resources.view,rowHeight:resources.view.rowHeight||this.rowH,labels:{...this.view.labels,...resources.view.labels}};const tree=this.tlTree=new TreeGrid(host,{view,toolbar:false,renderers,labels:this.opts.labels});tree.emit=(name,detail)=>{if(!tree.destroyed)host.dispatchEvent(new CustomEvent("canopyx:"+name,{detail}))};const render=tree.render.bind(tree);tree.render=()=>{const result=render();if(!this.tlRows||this.tlRows!==tree.rows.length+":"+tree.version){this.tlLearnLoaded()}this.tlPaintLanes();this.tlCheckWindow();return result};const signal=this.lifetime.signal;let syncing=false;main.addEventListener("scroll",()=>{if(!syncing&&tree.grid.scrollTop!==main.scrollTop){syncing=true;tree.grid.scrollTop=main.scrollTop;syncing=false}
28
+ root.style.setProperty("--cx-tl-side",resources.width+"px");main.append(head,canvas);side.appendChild(host);root.append(side,main);const aggregates=resources.aggregates,renderers={};for(const[column,kind]of Object.entries(aggregates))renderers[column]=(node,element)=>{setText(element,this.tlAggregateText(node.id,kind));element.classList.add("cx-tl-agg");return true};const view={...resources.view,rowHeight:this.view.plan?Math.max(64,resources.view.rowHeight||this.rowH):resources.view.rowHeight||this.rowH,labels:{...this.view.labels,...resources.view.labels}};const tree=this.tlTree=new TreeGrid(host,{view,toolbar:false,renderers,labels:this.opts.labels});main.setAttribute("aria-controls",tree.grid.id);tree.emit=(name,detail)=>{if(!tree.destroyed)host.dispatchEvent(new CustomEvent("canopyx:"+name,{detail}))};const render=tree.render.bind(tree);tree.render=()=>{const result=render();if(!this.tlRows||this.tlRows!==tree.rows.length+":"+tree.version){this.tlLearnLoaded()}this.tlPaintLanes();this.tlCheckWindow();return result};const signal=this.lifetime.signal;let syncing=false;main.addEventListener("scroll",()=>{if(!syncing&&tree.grid.scrollTop!==main.scrollTop){syncing=true;tree.grid.scrollTop=main.scrollTop;syncing=false}
28
29
  this.tlScheduleLanes();this.tlCheckWindow()},{passive:true,signal});tree.grid.addEventListener("scroll",()=>{if(!syncing&&Math.abs(tree.grid.scrollTop-main.scrollTop)>1){syncing=true;main.scrollTop=tree.grid.scrollTop;syncing=false}},{passive:true,signal});host.addEventListener("wheel",event=>{if(!event.deltaY||event.ctrlKey)return;event.preventDefault();main.scrollTop +=event.deltaY*(event.deltaMode===1?tree.rowH:event.deltaMode===2?main.clientHeight:1)},{passive:false,signal});host.addEventListener("canopyx:toggle",event=>{const node=event.detail.node;if(event.detail.expanded)this.calCollapsed.delete(node.id);else this.calCollapsed.add(node.id);this.stateChanged()},{signal});host.addEventListener("canopyx:select",event=>{const node=event.detail.node;if(!node){this.clearCalendarSelection();return}
29
30
  const zone=this.calZone,range=this.calRange,ids=new Set([node.id,...this.tlDescendants(node.id)]);this.calSelection={first:range.firstDay,last:range.lastDay,from:zone.start(range.firstDay),to:zone.start(range.lastDay+1),resource:node.id};const events=this.calEvents.filter(item=>!item.background&&this.calEventResources(item).some(id=>ids.has(id)));this.calAnnounce(fill(this.calLabels.calendarSelected,{range:this.calResourceName(node.id)}));this.calEmitLink("select",events,this.calResourceName(node.id),this.calSelection)},{signal});signal.addEventListener("abort",()=>{clearTimeout(this.tlWindowTimer);tree.destroy()},{once:true});tree.ready.then(()=>{if(!this.destroyed){this.calApplyCollapsed();this.tlPaintLanes()}},error=>this.emit("error",{error}))},tlLearnLoaded(){const tree=this.tlTree;this.tlRows=tree.rows.length+":"+tree.version;for(const row of tree.rows)if(!row.status&&row.key!=null&&!this.tl.learned.has(row.key)){this.tl.learned.add(row.key);this.tl.names.set(row.key,row.h.label??row.key);this.tl.order.push(row.key);const parent=row.parent?.key??null;this.tl.parent.set(row.key,parent);if(parent!==null){if(!this.tl.children.has(parent))this.tl.children.set(parent,[]);this.tl.children.get(parent).push(row.key)}
30
31
  if(row.h.hasChildren&&!this.tl.children.has(row.key))this.tl.children.set(row.key,[])}},tlDescendants(id){const out=[],stack=[...(this.tl.children.get(id)||[])];while(stack.length&&out.length<100000){const next=stack.pop();out.push(next);stack.push(...(this.tl.children.get(next)||[]))}
31
- return out},calApplyCollapsed(){const tree=this.tlTree;if(!tree||this.destroyed)return;const expanded=[];if(this.tlInline){const walk=(id,path)=>{if(!this.tl.children.has(id)||this.calCollapsed.has(id))return;expanded.push(path);for(const child of this.tl.children.get(id))walk(child,path.concat(child))};for(const id of this.tl.order)if(this.tl.parent.get(id)===null)walk(id,[id]);tree.setState({expanded}).catch?.(()=>{})}},calPaintTimeline(){this.tlEnsure();const span=this.calRange,axis=this.tlAxis=tlAxis(this.calZoom,span,this.calZone,this.cal.firstDay);this.tlIndex=this.tlIndexEvents();this.tl.heat.clear();if(this.tlRoot.parentNode!==this.calBody)this.calBody.replaceChildren(this.tlRoot);this.tlCanvas.style.width=axis.total+"px";this.tlHead.style.width=axis.total+"px";const header=tlHeader(this,axis),paint=(row,cells,cls)=>{row.replaceChildren(...cells.map(cell=>{const element=h("div",cls +(cell.weekend?" cx-tl-weekend":"")),text=h("span","cx-tl-hlabel");element.style.insetInlineStart=cell.x+"px";element.style.width=cell.width+"px";setText(text,cell.text);element.appendChild(text);element.title=cell.label;return element}))};paint(this.tlUpper,header.upper,"cx-tl-hcell cx-tl-hupper");paint(this.tlLower,header.lower,"cx-tl-hcell");this.tlHeader=header;this.calGrid=this.tlCanvas;if(this.tlScrolledFor!==this.calZoom+":"+span.firstDay){this.tlScrolledFor=this.calZoom+":"+span.firstDay;const now=Date.now(),target=now>=axis.from&&now<axis.to?axis.x(now)- this.tlMain.clientWidth / 3 : 0;
32
+ return out},calApplyCollapsed(){const tree=this.tlTree;if(!tree||this.destroyed)return;const expanded=[];if(this.tlInline){const walk=(id,path)=>{if(!this.tl.children.has(id)||this.calCollapsed.has(id))return;expanded.push(path);for(const child of this.tl.children.get(id))walk(child,path.concat(child))};for(const id of this.tl.order)if(this.tl.parent.get(id)===null)walk(id,[id]);tree.setState({expanded}).catch?.(()=>{})}},calPaintTimeline(){this.tlEnsure();if(this.tlRoot.parentNode!==this.calBody)this.calBody.replaceChildren(this.tlRoot);const span=this.calRange,axis=this.tlAxis=tlAxis(this.calZoom,span,this.calZone,this.cal.firstDay,this.view.plan?this.tlMain.clientWidth:0);if(this.view.plan)this.planFitWidth=this.tlMain.clientWidth;this.tlIndex=this.tlIndexEvents();this.tl.heat.clear();if(this.tlRoot.parentNode!==this.calBody)this.calBody.replaceChildren(this.tlRoot);this.tlCanvas.style.width=axis.total+"px";this.tlHead.style.width=axis.total+"px";const header=tlHeader(this,axis),paint=(row,cells,cls)=>{row.replaceChildren(...cells.map(cell=>{const element=h("div",cls +(cell.weekend?" cx-tl-weekend":"")),text=h("span","cx-tl-hlabel");element.style.insetInlineStart=cell.x+"px";element.style.width=cell.width+"px";setText(text,cell.text);element.appendChild(text);element.title=cell.label;return element}))};paint(this.tlUpper,header.upper,"cx-tl-hcell cx-tl-hupper");paint(this.tlLower,header.lower,"cx-tl-hcell");this.tlHeader=header;this.calGrid=this.tlCanvas;if(this.tlScrolledFor!==this.calZoom+":"+span.firstDay){this.tlScrolledFor=this.calZoom+":"+span.firstDay;const now=Date.now(),target=now>=axis.from&&now<axis.to?axis.x(now)- this.tlMain.clientWidth / 3 : 0;
32
33
  this.tlScrollTo(Math.max(0,target))}
33
34
  this.tlTree.version++;this.tlTree.scheduleRender();this.tlPaintLanes()},tlScrollX(){return Math.abs(this.tlMain.scrollLeft)},tlScrollTo(x){this.tlMain.scrollLeft=this.calRtl?-x:x},calTimelineScroll(){return this.tlMain?{timeline:true,top:this.tlMain.scrollTop,left:this.tlScrollX(),view:"timeline",date:this.calDate,zoom:this.calZoom}:null},calRestoreTimelineScroll(state){if(state.zoom!==this.calZoom||state.date!==this.calDate)return false;this.tlMain.scrollTop=state.top;this.tlScrollTo(state.left);return true}});Object.assign(DataGrid.prototype,{calWindowTimeColumns(columns){this.calTimeWindow=null;if(this.calPhone||this.calPrinting||columns.length<=24)return columns;const size=Math.max(2,Math.min(24,Math.floor((this.calBody.clientWidth-this.calKit.CAL_GUTTER)/ 150)));
34
35
  const first=Math.max(0,Math.min(columns.length-size,this.calTimeFirst||0));this.calTimeFirst=first;this.calTimeWindow={first,size,total:columns.length,columns};return columns.slice(first,first+size).map((column,index)=>({...column,index:first+index}))},calTimeReveal(index,minutes){const window=this.calTimeWindow;if(!window||index<0||index>=window.total)return false;const column=window.columns[index];this.calTimeFirst=index<window.first?index:index>=window.first+window.size?index-window.size+1:window.first;this.calFocus={day:column.day,minutes,resource:column.resource,event:null};this.calPaint({keep:true});this.calRestoreFocus(true);return true}});const TL_MARGIN=480,TL_STACK=4,TL_MIN_BAR=8,TL_SOLID_BAR=16;Object.assign(DataGrid.prototype,{tlIndexEvents(){const byResource=new Map(),backgrounds=new Map();for(const event of this.calEvents){const span=this.calDisplaySpan(event);event.tlStart=span.start;event.tlEnd=span.end}
@@ -41,7 +42,7 @@ for(const event of events){let lane=ends.findIndex(end=>end<=event.tlStart);if(l
41
42
  ends[lane]=Math.max(event.tlEnd,event.tlStart+1);const key="lane-"+lane;event.tlLane=0;event.tlRow=key;if(!byResource.has(key)){const list=[];list.lanes=1;byResource.set(key,list)}
42
43
  byResource.get(key).push(event)}
43
44
  const count=Math.max(1,ends.length);tree.rows=Array.from({length:count},(_,index)=>({key:"lane-"+index,level:1,status:null,h:{label:String(index+1)}}));tree.tops=Array.from({length:count+1},(_,index)=>index*tree.rowH);for(const row of tree.rows)this.tl.names.set(row.key,row.h.label);tree.version++;this.tl.stats=null;return{byResource,backgrounds}},tlVisibleRows(margin){const tree=this.tlTree,tops=tree.tops,count=tree.rows.length,main=this.tlMain;if(!count)return{first:0,last:-1};const top=main.scrollTop,bottom=top+main.clientHeight,find=y=>{let low=0,high=count-1;while(low<high){const mid=(low+high+1)>>1;if(tops[mid]<=y)low=mid;else high=mid-1}return low};return{first:Math.max(0,find(top)- margin),last:Math.min(count-1,find(bottom)+ margin)}},tlScheduleLanes(){if(this.tlFrame)return;this.tlFrame=requestAnimationFrame(()=>{this.tlFrame=0;if(!this.destroyed)this.tlPaintLanes()})},tlPaintLanes(){const tree=this.tlTree,axis=this.tlAxis,canvas=this.tlCanvas;if(!tree||!axis||this.calView!=="timeline"||!canvas.isConnected)return;const rows=tree.rows,tops=tree.tops,total=tops[rows.length]||0,main=this.tlMain;canvas.style.height=total+"px";canvas.setAttribute("aria-rowcount",String(rows.length));const visible=this.tlVisibleRows(3),bucket=Math.floor(this.tlScrollX()/ TL_MARGIN), left = (bucket - 1) * TL_MARGIN, right = (bucket + 2) * TL_MARGIN + main.clientWidth;
44
- const lanes=this.tlLanes||=new Map(),keep=new Set(),signature=[this.calEpoch,this.calVersion,this.calSourceVersion||0,axis.zoom,axis.from,bucket,this.linkHighlight?.size||0,this.calSelection?.resource??""].join("|");for(let i=visible.first;i<=visible.last;i++){const row=rows[i],key=row.status?"status:"+i:row.key;keep.add(key);let lane=lanes.get(key);if(!lane){lane=h("div","cx-tl-lane");lane.setAttribute("role","row");lanes.set(key,lane)}
45
+ const lanes=this.tlLanes||=new Map(),keep=new Set(),signature=[this.calEpoch,this.calVersion,this.calSourceVersion||0,axis.zoom,axis.from,axis.width,bucket,this.linkHighlight?.size||0,this.calSelection?.resource??""].join("|");for(let i=visible.first;i<=visible.last;i++){const row=rows[i],key=row.status?"status:"+i:row.key;keep.add(key);let lane=lanes.get(key);if(!lane){lane=h("div","cx-tl-lane");lane.setAttribute("role","row");lanes.set(key,lane)}
45
46
  const height=(tops[i+1]??total)- tops[i];lane.style.top=tops[i]+ "px";lane.style.height=height+"px";lane.setAttribute("aria-rowindex",String(i+1));if(lane._sig!==signature+"|"+height||lane._row!==row){lane._sig=signature+"|"+height;lane._row=row;this.tlPaintLane(lane,row,height,left,right)}
46
47
  if(lane.parentNode!==canvas)canvas.appendChild(lane)}
47
48
  for(const[key,lane]of lanes)if(!keep.has(key)){if(lane.contains(document.activeElement))continue;lane.remove();lanes.delete(key)}
@@ -1,4 +1,5 @@
1
- /* Graphite chrome shared by flat grids and trees: the card,toolbar,search,buttons,menus,header and cells. */
1
+ /* Graphite chrome shared by flat grids and trees: the card,toolbar,search,menus,header and cells. Buttons,fields,chips
2
+ and badges are the shared controls (canopyx.source/controls.css). */
2
3
  .cx-tree-tools{border:var(--cx-size-1px) solid var(--hfx-color-border,light-dark(#e2e4e7,#272a30));border-radius:var(--hfx-radius-container,var(--hfx-radius-lg,10px));overflow:hidden;font:var(--hfx-control-font-size,calc(var(--hfx-type-body-size,0.875rem) - var(--cx-compact,0) * .0625rem))/1.4 var(--hfx-font-sans,"Segoe UI Variable Text","Segoe UI",system-ui,-apple-system,"Helvetica Neue",Arial,sans-serif);}
3
4
  .cx-page > .cx-tree-tools,.cx-page > .cx-main > .cx-tree-tools{padding:0;}
4
5
  body .cx-host.cx-tree-tools{padding:0;}
@@ -11,14 +12,7 @@ body .cx-host.cx-tree-tools{padding:0;}
11
12
  .cx-tree-tools .cx-search:focus-within{outline:var(--hfx-focus-width,2px) solid var(--hfx-color-focus-ring,light-dark(#02726a,#37c9b8));outline-offset:var(--hfx-focus-offset,2px);}
12
13
  .cx-tree-tools .cx-search .cx-search-input:focus-visible{outline:0;}
13
14
  .cx-tree-tools .cx-search kbd{font:var(--cx-size-11px) var(--hfx-font-mono,"Cascadia Mono","Cascadia Code",Consolas,"SFMono-Regular",Menlo,monospace);border:var(--cx-size-1px) solid var(--hfx-color-border,light-dark(#e2e4e7,#272a30));border-radius:var(--hfx-radius-control,var(--hfx-radius-md,6px));padding:0 var(--cx-size-5px);}
14
- .cx-tree-tools .cx-button,.cx-tree-tools .cx-btn{height:var(--hfx-control-height-md,calc(36px - var(--cx-compact,0) * 8px));min-height:var(--hfx-control-height-md,calc(36px - var(--cx-compact,0) * 8px));display:inline-flex;align-items:center;justify-content:center;gap:var(--cx-size-6px);padding:0 var(--cx-size-11px);border:var(--cx-size-1px) solid var(--hfx-color-border-strong,light-dark(#cdd0d5,#363a42));border-radius:var(--hfx-radius-control,var(--hfx-radius-md,6px));background:var(--hfx-color-surface,light-dark(#ffffff,#16181c));color:var(--hfx-color-text,light-dark(#16181c,#f2f3f5));font:var(--hfx-weight-medium,500) var(--hfx-control-font-size,calc(var(--hfx-type-body-size,0.875rem) - var(--cx-compact,0) * .0625rem)) var(--hfx-font-sans,"Segoe UI Variable Text","Segoe UI",system-ui,-apple-system,"Helvetica Neue",Arial,sans-serif);cursor:pointer;white-space:nowrap;}
15
15
  .cx-tree-tools .cx-columns > summary::after{display:none;}
16
- .cx-tree-tools .cx-button:hover,.cx-tree-tools .cx-btn:hover{background:var(--hfx-color-hover,light-dark(#f5f5f5,#232529));}
17
- .cx-tree-tools .ghost{border-color:transparent;color:var(--hfx-color-text-secondary,light-dark(#4d525b,#b0b4bc));}
18
- .cx-tree-tools .icon{width:var(--hfx-control-height-md,calc(36px - var(--cx-compact,0) * 8px));padding:0;}
19
- .cx-tree-tools .active{border-color:var(--hfx-color-accent,light-dark(#11776f,#37c9b8));color:var(--hfx-color-accent-text,light-dark(#02726a,#37c9b8));background:var(--hfx-color-accent-subtle,light-dark(#e7f1f1,#1b3435));}
20
- .cx-tree-tools .badge{min-width:var(--cx-size-18px);height:var(--cx-size-18px);padding:0 var(--cx-size-5px);border-radius:var(--hfx-radius-badge,var(--hfx-radius-sm,4px));background:var(--hfx-color-accent,light-dark(#11776f,#37c9b8));color:var(--hfx-color-accent-contrast,light-dark(#ffffff,#0c0d10));font-size:var(--cx-size-11d5px);font-weight:700;display:inline-grid;place-items:center;}
21
- .cx-tree-tools .cx-button:disabled{opacity:.4;cursor:default;}
22
16
  .cx-spacer{flex:1;}
23
17
  .cx-sep{width:var(--cx-size-1px);height:var(--cx-size-20px);background:var(--hfx-color-border,light-dark(#e2e4e7,#272a30));}
24
18
  .cx-tree-tools .cx-hrow{height:var(--hfx-table-header-height,calc(40px - var(--cx-compact,0) * 8px));}
@@ -39,6 +33,18 @@ body .cx-host.cx-tree-tools{padding:0;}
39
33
  .cx-grid-delivery-status{margin:0;font-size:var(--cx-size-d875em);overflow-wrap:anywhere;}
40
34
  .cx-grid-delivery-status:empty{display:none;}
41
35
  .cx-grid-export progress{width:100%;max-width:100%;accent-color:var(--hfx-color-accent,light-dark(#11776f,#37c9b8));}
36
+ .cx-tree-tools .cx-export-panel{display:flex;flex-direction:column;flex-wrap:nowrap;width:min(44ch,calc(100vw - var(--hfx-space-4,1rem)));gap:var(--hfx-space-2,0.5rem);overflow:auto;}
37
+ .cx-tree-tools .cx-export-panel > *{flex-shrink:0;}
38
+ .cx-tree-tools.cx-phone .cx-grid-panel.cx-export-panel::before{flex:0 0 var(--hfx-space-1,0.25rem);}
39
+ .cx-export-count{margin:0;padding:0 var(--hfx-space-0-5,2px) var(--hfx-space-1,0.25rem);color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));font-size:var(--hfx-type-caption-size,0.75rem);}
40
+ .cx-export-formats,.cx-export-actions{display:flex;flex-direction:column;gap:var(--hfx-space-0-5,2px);}
41
+ .cx-export-actions{border-top:var(--hfx-border-width,1px) solid var(--hfx-color-border,light-dark(#e2e4e7,#272a30));padding-top:var(--hfx-space-2,0.5rem);margin-top:var(--hfx-space-1,0.25rem);}
42
+ .cx-tree-tools .cx-export-choice{width:100%;height:auto;min-height:var(--hfx-control-height-lg,calc(44px - var(--cx-compact,0) * 8px));justify-content:flex-start;text-align:left;padding:var(--hfx-space-2,0.5rem) var(--hfx-space-2,0.5rem);gap:var(--hfx-space-3,0.75rem);border:0;background:transparent;white-space:normal;}
43
+ .cx-export-icon{flex:none;display:grid;place-items:center;box-sizing:border-box;width:calc(var(--hfx-icon-md,16px)*2.5);height:var(--hfx-menu-item-height,calc(32px - var(--cx-compact,0) * 4px));padding:var(--hfx-space-1,0.25rem);border:var(--hfx-border-width,1px) solid var(--hfx-color-border,light-dark(#e2e4e7,#272a30));border-radius:var(--hfx-radius-control,var(--hfx-radius-md,6px));color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));font:var(--hfx-weight-semibold,600) var(--hfx-type-caption-size,0.75rem) var(--hfx-font-sans,"Segoe UI Variable Text","Segoe UI",system-ui,-apple-system,"Helvetica Neue",Arial,sans-serif);}
44
+ .cx-export-actions .cx-export-icon{font-size:var(--hfx-icon-md,16px);}
45
+ .cx-export-choice-text{display:flex;flex-direction:column;min-width:0;gap:var(--hfx-space-1,0.25rem);}
46
+ .cx-export-choice-title{font-weight:var(--hfx-weight-semibold,600);}
47
+ .cx-export-hint{font-size:var(--hfx-type-caption-size,0.75rem);font-weight:normal;color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));line-height:var(--hfx-type-caption-line-height,1.35);}
42
48
  .cx-tree-tools .cx-grid-views .cx-grid-field{width:100%;}
43
49
  .cx-tree-tools :is(button,summary,select,input,textarea):focus-visible{outline:var(--hfx-focus-width,2px) solid var(--hfx-color-focus-ring,light-dark(#02726a,#37c9b8));outline-offset:var(--hfx-focus-offset,2px);}
44
50
  .cx-tree-tools .cx-grid-overflow .cx-grid-panel{right:0;left:auto;width:var(--cx-size-244px);}
@@ -50,7 +56,7 @@ body .cx-host.cx-tree-tools{padding:0;}
50
56
  .cx-tree-tools.cx-phone .cx-grid-columns > summary .cx-ico{width:var(--cx-size-16px);height:var(--cx-size-16px);}
51
57
  .cx-tree-tools.cx-phone .cx-toolbar :is(.cx-btn,.cx-button){min-height:var(--cx-size-44px);}
52
58
  .cx-tree-tools.cx-phone .cx-grid-panel{position:fixed!important;inset:auto 0 0!important;width:100%!important;max-width:100%!important;max-height:var(--cx-size-85vh)!important;overflow:auto;border-radius:var(--hfx-radius-overlay,var(--hfx-radius-lg,10px));z-index:30;}
53
- .cx-tree-tools.cx-phone .cx-grid-panel::before{content:"";display:block;width:var(--cx-size-32px);height:var(--cx-size-4px);border-radius:var(--hfx-radius-pill,999px);background:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));margin:0 auto var(--cx-size-8px);flex-basis:var(--cx-size-32px);}
59
+ .cx-tree-tools.cx-phone .cx-grid-panel::before{content:"";display:block;width:var(--cx-size-32px);height:var(--cx-size-4px);border-radius:var(--hfx-radius-pill,999px);background:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));margin:0 auto var(--cx-size-8px);}
54
60
  .cx-tree-tools.cx-phone .cx-grid-panel:has(> [open])::before{display:none;}
55
61
  .cx-tree-tools.cx-phone .cx-hrow{height:var(--cx-size-44px);}
56
62
  .cx-tree-tools.cx-phone .cx-hidden-fields::after{content:"";position:absolute;inset:calc(-1 * var(--cx-size-10px)) calc(-1 * var(--cx-size-8px));}
@@ -60,36 +66,31 @@ body .cx-host.cx-tree-tools{padding:0;}
60
66
  .cx-grid-column-choice{display:flex;align-items:center;border-radius:var(--hfx-radius-sm,4px);}
61
67
  .cx-grid-column-choice label{flex:1;min-width:0;white-space:nowrap;}
62
68
  .cx-grid-column-name{flex:1;overflow:hidden;text-overflow:ellipsis;}
63
- :is(.cx-grid-column-note,.cx-grid-column-handle){flex:none;padding:0 var(--cx-size-6px);color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));font-size:var(--cx-size-12px);white-space:nowrap;}
64
- .cx-grid-column-handle{height:var(--cx-size-28px);border:0;background:none;cursor:grab;touch-action:none;}
65
- .cx-grid-column-choice.cx-drop-before{box-shadow:inset 0 var(--cx-size-2px) var(--hfx-color-accent,light-dark(#11776f,#37c9b8));}
66
- .cx-grid-column-choice.cx-drop-after{box-shadow:inset 0 var(--cx-size-neg-2px) var(--hfx-color-accent,light-dark(#11776f,#37c9b8));}
67
- .cx-grid-column-actions{display:flex;justify-content:space-between;margin-top:var(--cx-size-4px);padding-top:var(--cx-size-4px);border-top:var(--cx-size-1px) solid var(--hfx-color-border,light-dark(#e2e4e7,#272a30));}
69
+ :is(.cx-grid-column-note,.cx-grid-column-handle){flex:none;padding:0 var(--hfx-space-1-5,6px);color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));font-size:var(--hfx-type-caption-size,0.75rem);white-space:nowrap;}
70
+ .cx-grid-column-handle{height:calc(var(--hfx-space-1,0.25rem)*7);border:0;background:none;cursor:grab;touch-action:none;}
71
+ .cx-grid-column-choice.cx-drop-before{box-shadow:inset 0 var(--hfx-space-0-5,2px) var(--hfx-color-accent,light-dark(#11776f,#37c9b8));}
72
+ .cx-grid-column-choice.cx-drop-after{box-shadow:inset 0 calc(var(--hfx-space-0-5,2px)*-1) var(--hfx-color-accent,light-dark(#11776f,#37c9b8));}
73
+ .cx-grid-column-actions{display:flex;justify-content:space-between;margin-top:var(--hfx-space-1,0.25rem);padding-top:var(--hfx-space-1,0.25rem);border-top:var(--hfx-border-width,1px) solid var(--hfx-color-border,light-dark(#e2e4e7,#272a30));}
68
74
  .cx-grid-column-actions .cx-button{border:0;color:var(--hfx-color-accent-text,light-dark(#02726a,#37c9b8));background:none;}
69
- .cx-drawer{width:100%;max-width:var(--cx-size-440px);background:var(--hfx-color-surface,light-dark(#ffffff,#16181c));border:var(--cx-size-1px) solid var(--hfx-color-border,light-dark(#e2e4e7,#272a30));border-radius:var(--hfx-radius-overlay,var(--hfx-radius-lg,10px));box-shadow:var(--hfx-elevation-2,0 1px 2px rgba(22,24,28,.05),0 6px 18px -10px rgba(22,24,28,.18));font:var(--cx-size-14px)/1.45 var(--hfx-font-sans,"Segoe UI Variable Text","Segoe UI",system-ui,-apple-system,"Helvetica Neue",Arial,sans-serif);color:var(--hfx-color-text,light-dark(#16181c,#f2f3f5));overflow:hidden;}
70
- .cx-drawer-h{display:flex;align-items:flex-start;gap:var(--cx-size-12px);padding:var(--cx-size-14px) var(--cx-size-16px);border-bottom:var(--cx-size-1px) solid var(--hfx-color-border,light-dark(#e2e4e7,#272a30));}
71
- .cx-drawer-h h4{margin:0;font-size:var(--cx-size-17px);width:fit-content;}
72
- .cx-drawer-h p{color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));font-size:var(--cx-size-13px);}
73
- .cx-drawer-h .cx-btn{margin-left:auto;}
74
- .cx-dl{display:grid;grid-template-columns:var(--cx-size-140px) 1fr;gap:var(--cx-size-8px) var(--cx-size-16px);padding:var(--cx-size-14px) var(--cx-size-16px);margin:0;}
75
- .cx-dl dt{color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));font-size:var(--cx-size-13px);padding-top:var(--cx-size-1px);}
75
+ @layer hfx.components{
76
+ .cx-drawer-h h4{width:fit-content;}
77
+ .cx-drawer-h p{color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));font-size:var(--hfx-type-small-size,0.8125rem);}
78
+ .cx-drawer-h .cx-btn{margin-inline-start:auto;}
79
+ .cx-dl{display:grid;grid-template-columns:calc(var(--hfx-space-1,0.25rem)*35) 1fr;gap:var(--hfx-space-2,0.5rem) var(--hfx-space-4,1rem);padding:calc(var(--hfx-space-1,0.25rem)*3.5) 0;margin:0;}
80
+ .cx-dl dt{color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));font-size:var(--hfx-type-small-size,0.8125rem);padding-block-start:var(--hfx-border-width,1px);}
76
81
  .cx-dl dd{margin:0;}
77
- .cx-drawer-s{padding:var(--cx-size-4px) var(--cx-size-16px) 0;font-size:var(--cx-size-11d5px);letter-spacing:var(--cx-size-d06em);text-transform:uppercase;color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));font-weight:700;}
78
- body .cx-host.cx-drawer{position:fixed;left:auto;right:var(--cx-size-12px);top:var(--cx-size-12px);bottom:var(--cx-size-12px);margin:0;width:calc(100% - var(--cx-size-24px));max-width:var(--cx-size-440px);height:calc(100% - var(--cx-size-24px));padding:0;display:flex;flex-direction:column;z-index:1000;}
79
- .cx-drawer-body{overflow:auto;flex:1;}
80
- .cx-drawer .cx-drawer-h > div{flex:1;min-width:0;}
81
- .cx-drawer .cx-drawer-h{flex:none;}
82
- .cx-drawer .cx-drawer-h p{margin:var(--cx-size-4px) 0 0;overflow-wrap:anywhere;}
83
- .cx-drawer dd{overflow-wrap:anywhere;min-width:0;}
82
+ .cx-drawer-s{padding:var(--hfx-space-1,0.25rem) 0 0;font-size:calc(var(--hfx-type-caption-size,0.75rem)*.9583);letter-spacing:.06em;text-transform:uppercase;color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));font-weight:700;}
83
+ .cx-drawer .cx-drawer-h > div{flex:1;min-inline-size:0;}
84
+ .cx-drawer .cx-drawer-h p{margin:var(--hfx-space-1,0.25rem) 0 0;overflow-wrap:anywhere;}
85
+ .cx-drawer dd{overflow-wrap:anywhere;min-inline-size:0;}
84
86
  .cx-drawer .cx-button{flex:none;}
85
- .cx-drawer :is(.p-strip,.p-spark,.p-line){max-width:100%;}
86
- body .cx-host.cx-drawer.cx-phone{inset:0;width:100%;max-width:none;height:100%;max-height:none;border-radius:var(--hfx-radius-none,0);}
87
- .cx-drawer.cx-phone .cx-dl{grid-template-columns:1fr;gap:var(--cx-size-2px) 0;}
88
- .cx-drawer.cx-phone dd{margin-bottom:var(--cx-size-8px);}
89
- .cx-drawer.cx-phone .cx-button{min-height:var(--cx-size-44px);min-width:var(--cx-size-44px);}
90
- .cx-drawer::backdrop{background:var(--hfx-color-overlay,light-dark(rgba(22,24,28,0.42),rgba(0,0,0,0.66)));}
87
+ .cx-drawer :is(.p-strip,.p-spark,.p-line){max-inline-size:100%;}
88
+ .cx-drawer[data-phone="true"] .cx-dl{grid-template-columns:1fr;gap:var(--hfx-space-0-5,2px) 0;}
89
+ .cx-drawer[data-phone="true"] dd{margin-block-end:var(--hfx-space-2,0.5rem);}
90
+ .cx-drawer[data-phone="true"] .cx-button{min-block-size:calc(var(--hfx-space-1,0.25rem)*11);min-inline-size:calc(var(--hfx-space-1,0.25rem)*11);}
91
+ }
91
92
  .cx-grid-overlay{color:var(--hfx-color-text,light-dark(#16181c,#f2f3f5));font:var(--hfx-control-font-size,calc(var(--hfx-type-body-size,0.875rem) - var(--cx-compact,0) * .0625rem))/1.45 var(--hfx-font-sans,"Segoe UI Variable Text","Segoe UI",system-ui,-apple-system,"Helvetica Neue",Arial,sans-serif);}
92
- body .cx-host.cx-grid-overlay{height:auto;min-height:0;padding:0;}
93
+ body .cx-host.cx-grid-overlay:not(.cx-drawer){height:auto;min-height:0;padding:0;}
93
94
  .cx-grid-overlay [hidden]{display:none!important;}
94
95
  .cx-tree-tools.cx-pin-tree .cx-row > .cx-cell.cx-tree:first-child{position:sticky;}
95
96
  .cx-tree-tools .cx-row > [data-cx-pin]{position:sticky;z-index:1;background:inherit;}
@@ -1,6 +1,9 @@
1
- (function(global){"use strict";const installed=new WeakSet();const part={version:"ee95c5cc39223ef4",install(context){if(installed.has(context.TreeGrid.prototype))return;const{TreeGrid,h,setText,fill,numberFormat,watchFlyout,columnWidth,prepareWidth,cellText,cellValue,isCellObject,foldText,formatting,sortKey,compareKeys,EXPORT_FORMATS,exportFormat,exportTimeout,exportFormatsOf,INDENT,MAX_PROTOCOL_INTEGER,hostControl,detailCell,HostCellContext,disposeHostSlot,loadPart,definePart,pluginItems,applyTone,toneOf,badgeElement,RECORD_IMPL,svgIcon,gridExportField,columnNumericTone,columnValueTone,overlayTheme}=context;const EXPORT_IMPL={formatsOf:exportFormatsOf};const GRID_ICONS={search:'<circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/>',filter:'<path d="M4 5h16l-6 7.5V19l-4-2v-4.5z"/>',columns:'<rect x="3.5" y="4.5" width="17" height="15" rx="2"/><path d="M9.5 4.5v15M14.5 4.5v15"/>',views:'<path d="M6 4h12v16l-6-4-6 4z"/>',export:'<path d="M12 4v11M7.5 10.5 12 15l4.5-4.5M5 19h14"/>',more:'<circle cx="5" cy="12" r="1.2"/><circle cx="12" cy="12" r="1.2"/><circle cx="19" cy="12" r="1.2"/>',close:'<path d="M6 6l12 12M18 6 6 18"/>',check:'<path d="m5 12.5 4.5 4.5L19 7.5"/>',chart:'<path d="M4 19h16M7 16V10M12 16V6M17 16v-4"/>',copy:'<rect x="8" y="8" width="11" height="11" rx="2"/><path d="M16 8V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h2"/>',up:'<path d="M12 19V5M6 11l6-6 6 6"/>',down:'<path d="M12 5v14M6 13l6 6 6-6"/>',retry:'<path d="M4 12a8 8 0 1 0 2.4-5.7M4 4v4.5h4.5"/>',previous:'<path d="m14 7-5 5 5 5"/>',next:'<path d="m10 7 5 5-5 5"/>',first:'<path d="m13 7-5 5 5 5M17 7l-5 5 5 5"/>',last:'<path d="m11 7 5 5-5 5M7 7l5 5-5 5"/>'};function gridIcon(name,shapes){return svgIcon(shapes||GRID_ICONS[name]||GRID_ICONS.more)}
1
+ (function(global){"use strict";const installed=new WeakSet();const part={version:"ab6490d0ae0e0aff",requires:["ui-overlays"],install(context){if(installed.has(context.TreeGrid.prototype))return;const{UI_IMPL,UI_TOOLS,TreeGrid,h,setText,fill,numberFormat,watchFlyout,columnWidth,prepareWidth,cellText,cellValue,isCellObject,foldText,formatting,sortKey,compareKeys,EXPORT_FORMATS,exportFormat,INDENT,MAX_PROTOCOL_INTEGER,hostControl,detailCell,HostCellContext,disposeHostSlot,loadPart,definePart,pluginItems,applyTone,toneOf,badgeElement,RECORD_IMPL,svgIcon,gridExportField,columnNumericTone,columnValueTone,overlayTheme}=context;const EXPORT_IMPL={};function exportTimeout(grid,options){const timeout=options?.timeoutMs??grid.opts.exportTimeoutMs??600000;if(!Number.isInteger(timeout)||timeout<1||timeout>2147483647)throw new Error("CanopyX: invalid export timeout.");return timeout}
2
+ const OFFICE_EXPORT_PART={name:"office-export",label:"Office export writers",parts:{},context:()=>({EXPORT_IMPL})};const officeExportWrite=(capture,context)=>loadPart(OFFICE_EXPORT_PART).then(()=>EXPORT_IMPL.office(capture,context));function officeExportFormats(grid){return[{id:"xlsx",label:grid.labels.gridDownloadXlsx,extension:"xlsx",values:"raw",mime:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",write:officeExportWrite},{id:"pdf",label:grid.labels.gridDownloadPdf,extension:"pdf",values:"raw",mime:"application/pdf",write:officeExportWrite}]}
3
+ EXPORT_IMPL.formatsOf=function(grid){const list=grid.opts.exportFormats??grid.view.exportFormats,known=new Map(EXPORT_FORMATS);if(list!==undefined)for(const format of officeExportFormats(grid))if(!known.has(format.id))known.set(format.id,exportFormat(format));for(const format of grid.plugins.filter(item=>(grid.opts.plugins||[]).some(local=>local.name===item.name))
4
+ .flatMap(item=>item.exportFormats||[]))known.set(format.id,exportFormat(format));if(list===undefined)return[...known.values()];const formats=Array.isArray(list)?list.map(item=>typeof item==="string"?known.get(item):exportFormat(item)).filter(Boolean):null;if(!formats||new Set(formats.map(format=>format.id)).size!==formats.length)throw new Error("CanopyX: invalid exportFormats.");return formats};const GRID_ICONS={search:'<circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/>',filter:'<path d="M4 5h16l-6 7.5V19l-4-2v-4.5z"/>',columns:'<rect x="3.5" y="4.5" width="17" height="15" rx="2"/><path d="M9.5 4.5v15M14.5 4.5v15"/>',views:'<path d="M6 4h12v16l-6-4-6 4z"/>',export:'<path d="M12 4v11M7.5 10.5 12 15l4.5-4.5M5 19h14"/>',more:'<circle cx="5" cy="12" r="1.2"/><circle cx="12" cy="12" r="1.2"/><circle cx="19" cy="12" r="1.2"/>',close:'<path d="M6 6l12 12M18 6 6 18"/>',check:'<path d="m5 12.5 4.5 4.5L19 7.5"/>',chart:'<path d="M4 19h16M7 16V10M12 16V6M17 16v-4"/>',copy:'<rect x="8" y="8" width="11" height="11" rx="2"/><path d="M16 8V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h2"/>',up:'<path d="M12 19V5M6 11l6-6 6 6"/>',down:'<path d="M12 5v14M6 13l6 6 6-6"/>',retry:'<path d="M4 12a8 8 0 1 0 2.4-5.7M4 4v4.5h4.5"/>',previous:'<path d="m14 7-5 5 5 5"/>',next:'<path d="m10 7 5 5-5 5"/>',first:'<path d="m13 7-5 5 5 5M17 7l-5 5 5 5"/>',last:'<path d="m11 7 5 5-5 5M7 7l5 5-5 5"/>'};function gridIcon(name,shapes){return svgIcon(shapes||GRID_ICONS[name]||GRID_ICONS.more)}
2
5
  function gridDecorateButton(button,icon,ghost){button.classList.add("cx-btn");if(ghost)button.classList.add("ghost");if(icon)button.prepend(gridIcon(icon));return button}
3
- function gridFilterCount(query){const count=group=>group?group.filters.length+group.groups.reduce((sum,child)=>sum+count(child),0):0;return(query?.filters?.length||0)+ count(query?.filterGroup)}
6
+ function gridFilterCount(query){const count=group=>group?group.filters.length+group.groups.reduce((sum,child)=>sum+count(child),0):0;return(query?.filters?.length||0)+ count(query?.filterGroup)+(query?.tupleFilters?.length||0)}
4
7
  function gridPriority(value){return["essential","normal","optional"].includes(value)||Number.isInteger(value)&&value>=0&&value<=MAX_PROTOCOL_INTEGER}
5
8
  function gridRank(column){const value=column.priority;return typeof value==="number"?value:value==="optional"?10000:value==="essential"||value===undefined&&column.identity?0:5000}
6
9
  function gridLevel(column){const rank=gridRank(column);return rank<2?0:rank<10000?1:2}
@@ -36,23 +39,24 @@ gridTextual(column)&&gridLevel(column)===1).sort((a,b)=>b.need-a.need)[0];grow(i
36
39
  grow(identity,Infinity);grow(widest,Infinity);return target},fitHide(shown,width,phone,size=column=>this.fitSize(column,1),level=!phone){let total=0,remaining=shown.length;const order=(a,b)=>gridLevel(b)- gridLevel(a)||gridRank(b)- gridRank(a)||shown.indexOf(b)- shown.indexOf(a),identity=phone&&shown.find(column=>column.identity);const hide=(column,pinned)=>!column.fixed&&!column.sized&&!column.alwaysVisible&&(column.pin==="none")!==pinned&&!(phone&&column.identity)&&gridLevel(column)>level;for(const column of this.columns)column.fitHidden=false;if(this.layoutMode&&this.layoutMode!=="fit")return;for(const column of shown)total +=size(column);for(const column of shown.filter(column=>hide(column,false)).sort(order))
37
40
  if(total>width&&remaining>1){column.fitHidden=true;total -=size(column);remaining--}
38
41
  if(identity&&!identity.fixed&&!identity.sized)total +=Math.min(identity.need??identity.width,(identity.indentNeed||0)+ 2*(this.fitCh||88))+(identity.reserve||0)- size(identity);if(identity)for(const column of shown.filter(column=>hide(column,true)).sort(order))
39
- if(total>width){column.fitHidden=true;total -=size(column)}}};function gridButton(text,run){const button=h("button","cx-button");button.type="button";setText(button,text);button.onclick=event=>{button.focus({preventScroll:true});run(event)};return button}
42
+ if(total>width){column.fitHidden=true;total -=size(column)}}};function gridVariant(button,variant){button.dataset.variant=variant;return button}
43
+ function gridButton(text,run){const button=h("button","cx-button");button.type="button";setText(button,text);button.onclick=event=>{button.focus({preventScroll:true});run(event)};return button}
40
44
  let keyboardInput=false;if(typeof document!=="undefined"){document.addEventListener("keydown",()=>{keyboardInput=true},true);document.addEventListener("pointerdown",()=>{keyboardInput=false},true)}
41
45
  const focusDrawerTitle=title=>{if(keyboardInput&&title===document.activeElement&&!title.matches(":focus-visible"))title.blur();title.toggleAttribute("data-cx-pointer-focus",!keyboardInput);if(!keyboardInput)title.addEventListener("blur",()=>title.removeAttribute("data-cx-pointer-focus"),{once:true});title.focus({preventScroll:true,focusVisible:keyboardInput})};function gridInputLabel(text,input){const label=h("label","cx-grid-field");label.appendChild(document.createTextNode(text));label.appendChild(input);return label}
42
46
  function gridOption(value,text){const option=h("option");option.value=value;setText(option,text);return option}
43
- const MENU_EVENT="canopyx:closemenus",announceMenu=source=>document.dispatchEvent(new CustomEvent(MENU_EVENT,{detail:{source}}));const TOOLBAR_PANELS={startMenus(){this.controlFlyouts=[];this.disposeControls=()=>this.controlFlyouts.forEach(flyout=>flyout.dispose());let pressed=null;for(const type of["mouseup","click","keydown","pointercancel"])document.addEventListener(type,()=>{pressed=null},{capture:true,signal:this.lifetime.signal});for(const type of["pointerdown","focusin",MENU_EVENT])document.addEventListener(type,event=>{if(type==="pointerdown")pressed=event.target;const node=type==="focusin"&&pressed?.isConnected&&event.target.contains(pressed)?pressed:event.detail?.source||event.target;if(type!==MENU_EVENT||event.target.contains(this.host))this.closeMenusOutside(node)},{signal:this.lifetime.signal})},closeMenusOutside(node){for(const details of this.toolbar.querySelectorAll(".cx-columns[open]"))if(!details.contains(node))details.open=false;const popover=this.gridPopover;if(popover&&!popover.panel.contains(node)&&!popover.anchor?.contains(node))this.closeGridPopover(false)},closeMenus(){this.closeMenusOutside(document)},controlPanel(title,className,onOpen){const details=h("details","cx-columns "+className),summary=h("summary","cx-btn"),panel=h("div","cx-columns-list cx-grid-panel cx-pop");summary.setAttribute("role","button");if(className!=="cx-grid-filters")summary.classList.add("ghost");setText(summary,title);details.append(summary,panel);this.toolbar.appendChild(details);summary.prepend(gridIcon(className.slice(8).replace("rs","r")));panel.popover="manual";const flyout=watchFlyout(details,panel,()=>details.open);this.controlFlyouts.push(flyout);details.addEventListener("toggle",()=>{if(details.open){announceMenu(details);if(panel.isConnected)panel.showPopover?.();if(onOpen)onOpen(panel);flyout.position()}else{panel.hidePopover?.();for(const inner of panel.querySelectorAll("details[open]"))inner.open=false}});details.addEventListener("keydown",event=>{if(event.key==="Escape"&&details.open){details.open=false;summary.focus();event.preventDefault();event.stopPropagation()}});return{details,panel}},buildSavedViews(){const panel=this.controlPanel(this.labels.gridViews,"cx-grid-views").panel;const choice=this.savedChoice=h("select"),name=h("input");name.type="text";name.maxLength=100;choice.setAttribute("aria-label",this.labels.gridChooseView);panel.appendChild(choice);panel.appendChild(gridInputLabel(this.labels.gridViewName,name));const error=h("p","cx-grid-control-error");error.setAttribute("role","status");const run=action=>{try{Promise.resolve(action()).then(()=>setText(error,""),failure=>setText(error,failure.message))}
44
- catch(failure){setText(error,failure.message)}};panel.appendChild(gridButton(this.labels.gridApply,()=>run(()=>this.applySavedView(choice.value))));panel.appendChild(gridButton(this.labels.gridSaveView,()=>run(()=>this.saveView(name.value))));panel.appendChild(gridButton(this.labels.gridRemoveView,()=>run(()=>this.removeSavedView(choice.value))));panel.appendChild(error);this.syncSavedViews()},syncSavedViews(){if(!this.savedChoice)return;const selected=this.savedChoice.value;this.savedChoice.textContent="";this.savedChoice.appendChild(gridOption("",this.labels.gridChooseView));this.getSavedViews().forEach(name=>this.savedChoice.appendChild(gridOption(name,name)));if(this.savedViews.has(selected))this.savedChoice.value=selected},placeMenus(phone){if(this.overflow)for(const selector of[".cx-grid-views",".cx-grid-export"]){const item=this.toolbar.querySelector(selector);if(!item)continue;item.open=false;if(phone)this.overflow.panel.appendChild(item);else this.toolbar.insertBefore(item,selector===".cx-grid-views"?this.slots?.afterViews||this.spacer:this.overflow.details)}}};const COLUMNS_MENU={moveColumnChoice(column,target,after){column.pin=target.pin;const ids=this.columns.map(item=>item.id).filter(id=>id!==column.id);ids.splice(ids.indexOf(target.id)+ after,0,column.id);this.setColumnOrder(ids)},renderGridColumns(panel){if(!this.columnList){const find=this.columnFind=h("input","cx-grid-column-find"),actions=h("div","cx-grid-column-actions");find.type="search";find.placeholder=this.labels.gridFindColumn;find.setAttribute("aria-label",find.placeholder);find.hidden=this.columns.length<9;find.oninput=()=>this.syncColumnControls();actions.append(gridButton(this.labels.gridShowAllColumns,()=>this.showAllColumns()),gridButton(this.labels.gridResetColumns,()=>this.resetColumns()));panel.replaceChildren(find,this.columnList=h("div"),actions)}
47
+ const MENU_EVENT="canopyx:closemenus",announceMenu=source=>document.dispatchEvent(new CustomEvent(MENU_EVENT,{detail:{source}}));const TOOLBAR_PANELS={startMenus(){this.controlFlyouts=[];this.disposeControls=()=>this.controlFlyouts.forEach(flyout=>flyout.dispose());let pressed=null;for(const type of["mouseup","click","keydown","pointercancel"])document.addEventListener(type,()=>{pressed=null},{capture:true,signal:this.lifetime.signal});for(const type of["pointerdown","focusin",MENU_EVENT])document.addEventListener(type,event=>{if(type==="pointerdown")pressed=event.target;const node=type==="focusin"&&pressed?.isConnected&&event.target.contains(pressed)?pressed:event.detail?.source||event.target;if(type!==MENU_EVENT||event.target.contains(this.host))this.closeMenusOutside(node)},{signal:this.lifetime.signal})},closeMenusOutside(node){const holds=element=>element&&(UI_TOOLS.owns?.(element,node)??element.contains(node));if(this.pendingColumnMenu&&!holds(this.pendingColumnMenu.anchor))this.pendingColumnMenu=null;for(const details of this.toolbar.querySelectorAll(".cx-columns[open]"))if(!holds(details))details.open=false;const popover=this.gridPopover;if(popover&&!holds(popover.panel)&&!holds(popover.anchor))this.closeGridPopover(false);const cellMenu=this.cellMenuPanel;if(cellMenu&&!holds(cellMenu)&&!holds(this.cellMenuHit?.cell))this.cellMenuBinding?.close("menus",false,true)},closeMenus(){this.closeMenusOutside(document)},controlPanel(title,className,onOpen){const details=h("details","cx-columns "+className),summary=h("summary","cx-btn"),panel=h("div","cx-columns-list cx-grid-panel cx-pop");summary.setAttribute("role","button");panel.setAttribute("aria-label",title);if(className!=="cx-grid-filters")summary.classList.add("ghost");setText(summary,title);details.append(summary,panel);this.toolbar.appendChild(details);summary.prepend(gridIcon(className.slice(8).replace("rs","r")));panel.popover="manual";const flyout=watchFlyout(details,panel,()=>details.open);this.controlFlyouts.push(flyout);details.addEventListener("toggle",()=>{if(details.open){announceMenu(details);if(onOpen)onOpen(panel);flyout.open()}else{flyout.close();for(const inner of panel.querySelectorAll("details[open]"))inner.open=false}});details.addEventListener("keydown",event=>{if(event.key==="Escape"&&details.open){details.open=false;flyout.close();summary.focus();event.preventDefault();event.stopPropagation()}});return{details,panel}},buildSavedViews(){const panel=this.controlPanel(this.labels.gridViews,"cx-grid-views").panel;const choice=this.savedChoice=h("select"),name=h("input");name.type="text";name.maxLength=100;choice.setAttribute("aria-label",this.labels.gridChooseView);panel.appendChild(choice);panel.appendChild(gridInputLabel(this.labels.gridViewName,name));const error=h("p","cx-grid-control-error");error.setAttribute("role","status");const run=action=>{try{Promise.resolve(action()).then(()=>setText(error,""),failure=>setText(error,failure.message))}
48
+ catch(failure){setText(error,failure.message)}};panel.appendChild(gridButton(this.labels.gridApply,()=>run(()=>this.applySavedView(choice.value))));panel.appendChild(gridButton(this.labels.gridSaveView,()=>run(()=>this.saveView(name.value))));panel.appendChild(gridButton(this.labels.gridRemoveView,()=>run(()=>this.removeSavedView(choice.value))));panel.appendChild(error);this.syncSavedViews()},syncSavedViews(){if(!this.savedChoice)return;const selected=this.savedChoice.value;this.savedChoice.textContent="";this.savedChoice.appendChild(gridOption("",this.labels.gridChooseView));this.getSavedViews().forEach(name=>this.savedChoice.appendChild(gridOption(name,name)));if(this.savedViews.has(selected))this.savedChoice.value=selected},placeMenus(phone){if(this.overflow)for(const selector of[".cx-grid-views",".cx-grid-export"]){const item=this.toolbar.querySelector(selector);if(!item)continue;item.open=false;if(phone)this.overflow.panel.appendChild(item);else this.toolbar.insertBefore(item,selector===".cx-grid-views"?this.slots?.afterViews||this.spacer:this.overflow.details)}}};const COLUMNS_MENU={moveColumnChoice(column,target,after){column.pin=target.pin;const ids=this.columns.map(item=>item.id).filter(id=>id!==column.id);ids.splice(ids.indexOf(target.id)+ after,0,column.id);this.setColumnOrder(ids)},renderGridColumns(panel){if(!this.columnList){const find=this.columnFind=h("input","cx-grid-column-find"),actions=h("div","cx-grid-column-actions");find.type="search";find.placeholder=this.labels.gridFindColumn;find.setAttribute("aria-label",find.placeholder);find.hidden=this.columns.length<9;find.oninput=()=>this.syncColumnControls();actions.append(gridButton(this.labels.gridShowAllColumns,()=>this.showAllColumns()),gridButton(this.labels.gridResetColumns,()=>this.resetColumns()));if(this.view.calculatedColumnsEditable)actions.append(gridButton(this.labels.gridCalculationAdd,event=>this.openCalculatedColumn(event.currentTarget)));panel.replaceChildren(find,this.columnList=h("div"),actions)}
45
49
  this.syncColumnControls()},syncColumnControls(){const list=this.columnList,active=document.activeElement,query=foldText(this.columnFind?.value.trim()||"");if(!list)return;const focus=list.contains(active)&&active.closest(".cx-grid-column-choice").dataset.cxColumn,held=focus&&active.matches(".cx-grid-column-handle"),last=this.columns.filter(item=>!item.hidden).length<2;list.textContent="";for(const column of this.columns){const row=h("div","cx-grid-column-choice"),label=h("label"),input=h("input"),name=h("span","cx-grid-column-name"),handle=h("button","cx-grid-column-handle"),note=column.fitHidden||column.responsiveHidden?this.labels.gridResponsiveHidden:column.pin==="left"?this.labels.gridPinnedLeft:column.pin==="right"?this.labels.gridPinnedRight:"";input.type="checkbox";input.checked=!column.hidden;input.disabled=!column.hidden&&last;input.onchange=()=>{input.focus({preventScroll:true});this.setColumnVisible(column.id,input.checked)};setText(name,name.title=column.title);handle.type="button";setText(handle,"⋮⋮");handle.setAttribute("aria-label",fill(this.labels.gridReorderColumn,{column:column.title}));label.append(input,name);row.append(handle,label);row.hidden=!!query&&!foldText(column.title).includes(query);if(note){const span=h("span","cx-grid-column-note");setText(span,note);row.append(span)}
46
50
  if(this.openColumnMenu&&this.tools.columnMenus!==false){const menu=gridDecorateButton(gridButton("",()=>this.openColumnMenu(column.id,menu)),"more",true);menu.classList.add("cx-grid-column-options","icon");menu.setAttribute("aria-label",fill(this.labels.gridColumnMenu,{column:column.title}));menu.setAttribute("aria-haspopup","menu");row.append(menu)}
47
51
  row.onkeydown=event=>{const target=event.altKey&&this.columns[this.columns.indexOf(column)+(event.key==="ArrowUp"?-1:event.key==="ArrowDown"?1:NaN)];if(target){event.preventDefault();event.stopPropagation();this.moveColumnChoice(column,target,event.key==="ArrowDown")}};handle.onpointerdown=event=>{if(event.button)return;event.preventDefault();handle.focus();handle.setPointerCapture(event.pointerId);let target,after;const clear=()=>{for(const item of list.children)item.classList.remove("cx-drop-before","cx-drop-after")};handle.onpointermove=move=>{clear();const over=document.elementFromPoint(move.clientX,move.clientY)?.closest(".cx-grid-column-choice");target=over!==row&&list.contains(over)&&this.columns[[...list.children].indexOf(over)];if(target){const box=over.getBoundingClientRect();after=move.clientY>box.top+box.height / 2; over.classList.add(after ? "cx-drop-after" : "cx-drop-before"); }
48
52
  };handle.onpointerup=handle.onpointercancel=up=>{handle.onpointermove=handle.onpointerup=handle.onpointercancel=null;clear();if(up.type==="pointerup"&&target)this.moveColumnChoice(column,target,after)}};row.dataset.cxColumn=column.id;list.appendChild(row);if(focus===column.id)(held?handle:input).focus({preventScroll:true})}}};function gridOperationActive(grid,signal){if(grid.destroyed||signal?.aborted)throw new DOMException("Grid cancelled.","AbortError")}
49
- const exportPartReady=()=>typeof loadExportPart==="function"?loadExportPart():Promise.resolve();function exportAs(grid,format,options={}){const{onProgress,timeoutMs,download,...capture}=options;let provider,resolutionError;try{provider=EXPORT_IMPL.formatsOf(grid).find(item=>item.id===format)}
53
+ const exportPartReady=()=>typeof loadExportPart==="function"?loadExportPart():Promise.resolve();function exportAs(grid,format,options={}){const{onProgress,onDiagnostic,includeMetadata,timeoutMs,download,...capture}=options;let provider,resolutionError;try{provider=EXPORT_IMPL.formatsOf(grid).find(item=>item.id===format)}
50
54
  catch(error){resolutionError=error}
51
- const prepared=grid.calIcsFormatWriter&&provider?.write===grid.calIcsFormatWriter?grid.calPrepareIcsExport(capture):grid.prepareExport(capture);return exportPartReady().then(()=>{if(resolutionError)throw resolutionError;return EXPORT_IMPL.run(grid,format,options,prepared,provider)})}
55
+ if(capture.values===undefined&&provider?.values!==undefined)capture.values=provider.values;const prepared=grid.calIcsFormatWriter&&provider?.write===grid.calIcsFormatWriter?grid.calPrepareIcsExport(capture):grid.prepareExport(capture);return exportPartReady().then(()=>{if(resolutionError)throw resolutionError;return EXPORT_IMPL.run(grid,format,options,prepared,provider)})}
52
56
  Object.assign(TreeGrid.prototype,TOOLBAR_PANELS,COLUMNS_MENU,{installTools(){this.checkTreeLayout();const bar=this.toolbar,tools=this.tools,labels=this.labels;this.host.classList.add("cx-chrome","cx-tree-tools");this.exportScopes=[["visible","gridVisibleRows"],["selected","gridSelectedRows"],["all","gridAllRows"]];this.toolbar.querySelector(".cx-search")?.prepend(gridIcon("search"));this.startMenus();this.lifetime.signal.addEventListener("abort",()=>{this.disposeControls();this.host.classList.remove("cx-chrome","cx-tree-tools","cx-phone")},{once:true});if(tools.columns!==false&&this.columns.length>1)this.controlPanel(labels.columns,"cx-grid-columns",panel=>this.renderGridColumns(panel));for(const item of pluginItems(this,"toolbarItems"))bar.appendChild(this.treeAction(item));this.prepareSavedViews();if(tools.views!==false&&this.stateKey()){this.buildSavedViews();bar.querySelector(".cx-grid-views .cx-grid-panel").appendChild(gridButton(labels.gridResetView,()=>this.resetView().catch(()=>{})))}
53
57
  bar.appendChild(this.spacer=h("span","cx-spacer"));if(tools.export!==false)this.exportControl=this.controlPanel(labels.gridExport,"cx-grid-export",panel=>this.renderGridExport(panel));if(tools.more!==false)this.buildMoreMenu();if(bar.childElementCount===1)bar.replaceChildren();this.syncToolbar();this.startFit()},buildMoreMenu(){const overflow=this.overflow=this.controlPanel(this.labels.gridMore,"cx-grid-overflow"),summary=overflow.details.firstChild;summary.replaceChildren(gridIcon("more"));summary.classList.add("icon");summary.setAttribute("aria-label",this.labels.gridMore);for(const action of[...this.opts.actions||[],...pluginItems(this,"menuItems")])overflow.panel.appendChild(this.treeAction(action));if(this.columns.length>1)overflow.panel.appendChild(Object.assign(gridButton(this.labels.gridDetails,()=>
54
58
  this.openDetails((this.focused||this.rows.find(w=>this.selected.has(w)))?.key).catch(error=>this.emit("error",{error}))),{className:"cx-button cx-grid-details"}));overflow.panel.appendChild(Object.assign(gridButton(this.labels.gridPrint,()=>global.print()),{className:"cx-button cx-grid-print"}))},treeSelection(){return Array.from(this.selected,w=>w.h)},treeAction(action){if(!action||typeof action.label!=="string"||typeof action.run!=="function")throw new Error("CanopyX: actions need a label and handler.");return gridButton(action.label,()=>Promise.resolve().then(()=>
55
- action.run({nodes:this.treeSelection(),signal:this.dataLifetime.signal,api:this.host.canopyx})).catch(error=>this.emit("error",{error})))},closeMenusOutside(node){TOOLBAR_PANELS.closeMenusOutside.call(this,node);const panel=this.interactionPanel;if(panel?.getAttribute("role")==="menu"&&!panel.contains(node))this.closeInteractionPanel(false)},renderGridColumns(panel){COLUMNS_MENU.renderGridColumns.call(this,panel);this.columnList.setAttribute("role","group");this.columnList.setAttribute("aria-label",this.labels.visibleColumns)},syncColumnControls(){COLUMNS_MENU.syncColumnControls.call(this);const row=this.columnList?.firstChild;if(row){row.querySelector(".cx-grid-column-handle").hidden=true;row.querySelector("input").disabled=true}},syncPhone(){const phone=this.host.clientWidth<=640;if(phone===this.phone)return;if(this.hiddenFieldCount())this.version++;this.phone=phone;this.host.classList.toggle("cx-phone",phone);this.placeMenus(phone)}});const OPEN_PANEL=TreeGrid.prototype.openInteractionPanel;if(OPEN_PANEL)TreeGrid.prototype.openInteractionPanel=function(...args){const panel=OPEN_PANEL.apply(this,args);if(panel?.getAttribute("role")==="menu")announceMenu(panel);return panel};const TREE_ACTIVATION={activate:TreeGrid.prototype.activate,onDblClick:TreeGrid.prototype.onDblClick};Object.assign(TreeGrid.prototype,{rowActivated(w,keyboard){const callback=this.opts.onRowActivate;let go=this.host.dispatchEvent(new CustomEvent("canopyx:rowActivate",{detail:{id:w.key,node:w.h,keyboard},bubbles:true,cancelable:true}));if(typeof callback==="function"){try{go=callback(w.h,{keyboard})!==false&&go}catch(error){global.console?.error(error)}}
59
+ action.run({nodes:this.treeSelection(),signal:this.dataLifetime.signal,api:this.host.canopyx})).catch(error=>this.emit("error",{error})))},closeMenusOutside(node){TOOLBAR_PANELS.closeMenusOutside.call(this,node);const panel=this.interactionPanel;if(panel?.getAttribute("role")==="menu"&&!panel.contains(node)&&!this.rendered.get(this.panelNode)?.contains(node))this.closeInteractionPanel(false)},renderGridColumns(panel){COLUMNS_MENU.renderGridColumns.call(this,panel);this.columnList.setAttribute("role","group");this.columnList.setAttribute("aria-label",this.labels.visibleColumns)},syncColumnControls(){COLUMNS_MENU.syncColumnControls.call(this);const row=this.columnList?.firstChild;if(row){row.querySelector(".cx-grid-column-handle").hidden=true;row.querySelector("input").disabled=true}},syncPhone(){const phone=this.host.clientWidth<=640;if(phone===this.phone)return;if(this.hiddenFieldCount())this.version++;this.phone=phone;this.host.classList.toggle("cx-phone",phone);this.placeMenus(phone)}});const OPEN_PANEL=TreeGrid.prototype.openInteractionPanel;if(OPEN_PANEL)TreeGrid.prototype.openInteractionPanel=function(...args){const panel=OPEN_PANEL.apply(this,args);if(panel?.getAttribute("role")==="menu")announceMenu(panel);return panel};const TREE_ACTIVATION={activate:TreeGrid.prototype.activate,onDblClick:TreeGrid.prototype.onDblClick};Object.assign(TreeGrid.prototype,{rowActivated(w,keyboard){const callback=this.opts.onRowActivate;let go=this.host.dispatchEvent(new CustomEvent("canopyx:rowActivate",{detail:{id:w.key,node:w.h,keyboard},bubbles:true,cancelable:true}));if(typeof callback==="function"){try{go=callback(w.h,{keyboard})!==false&&go}catch(error){global.console?.error(error)}}
56
60
  return go},activate(w,enter){if(!enter||w.status||this.rowActivated(w,true))TREE_ACTIVATION.activate.call(this,w,enter)},onDblClick(event){const w=this.rowFromEvent(event);if(!w||w.status||hostControl(event.target)||event.target.closest(".cx-exp")||this.rowActivated(w,false))TREE_ACTIVATION.onDblClick.call(this,event)}});const TREE_FIT={};for(const name of["applyWidths","visibleColumns","setColumnWidth","startResize","buildHeader","render","getState","readState","applyStatePresentation"])
57
61
  TREE_FIT[name]=TreeGrid.prototype[name];const TREE_FIT_ROWS=2000;Object.assign(TreeGrid.prototype,COLUMN_FIT,{checkTreeLayout(){for(const[index,column]of this.view.columns.entries()){if(column.priority!==undefined&&!gridPriority(column.priority)||column.flex!==undefined&&!(typeof column.flex==="number"&&column.flex>=0&&column.flex<=100)||
58
62
  [column.fixed,column.alwaysVisible,column.autoWidth].some(value=>value!==undefined&&typeof value!=="boolean")||
@@ -87,10 +91,9 @@ cols.forEach((column,i)=>{const index=this.columns.indexOf(column);const value=t
87
91
  td.appendChild(content.cloneNode(true))}finally{disposeHostSlot(slot)}})});return table}});const TREE_DETAILS={paintNodeRow:TreeGrid.prototype.paintNodeRow,active:null};Object.assign(TreeGrid.prototype,{focusRing:focusDrawerTitle,hiddenFieldCount(){return this.columns.filter(column=>column.hidden||column.fitHidden).length},paintNodeRow(rowEl,w){TREE_DETAILS.paintNodeRow.call(this,rowEl,w);const slot=rowEl._cells[0],count=this.phone&&this.fitReady&&this.hiddenFieldCount();let button=slot.hiddenFields;if(!count){button?.remove();slot.hiddenFields=null;return}
88
92
  if(!button){button=slot.hiddenFields=gridButton("",()=>this.openDetails(button.dataset.cxRecord).catch(error=>this.emit("error",{error})));button.classList.add("cx-hidden-fields");button.tabIndex=-1;button.addEventListener("click",event=>event.stopPropagation())}
89
93
  if(button.parentNode!==slot.cell)slot.cell.appendChild(button);button.dataset.cxRecord=w.h.id;button.setAttribute("aria-label",this.labels.gridDetails);setText(button,"+"+count)},async openDetails(id,options){const node=this.keyMap.get(id)?.h;if(node&&this.recordNeeded(node))await this.requirePart("record-panel");return this.showDetails(id,options)},showDetails(id,options={}){const rows=this.rows.filter(w=>!w.status&&!w.h.detail),returnFocus=options.returnFocus||document.activeElement,labels=this.labels,owner=this.dataLifetime;let index=rows.findIndex(w=>w.key===id);const loose=index<0?options.node||this.keyMap.get(id)?.h:null;if(index<0&&!loose||this.destroyed)throw new Error("CanopyX: open the details of a loaded node.");if(typeof this.opts.onDetails==="function"){const node=JSON.parse(JSON.stringify(loose||rows[index].h)),controller=new AbortController();owner.signal.addEventListener("abort",()=>controller.abort(),{once:true});return Promise.resolve(this.opts.onDetails({node,signal:controller.signal,returnFocus}))}
90
- if(TREE_DETAILS.active){TREE_DETAILS.active._replaced=true;TREE_DETAILS.active.close();TREE_DETAILS.active.remove()}
91
- const dialog=TREE_DETAILS.active=this.recordDrawer=h("dialog","cx-host cx-tree-tools cx-drawer"),head=h("div","cx-drawer-h"),title=h("h4"),body=h("div","cx-drawer-body");dialog._returnFocus=returnFocus;overlayTheme(this.host,dialog);title.id=this.id+"-record-title";title.tabIndex=-1;title.setAttribute("aria-level","2");dialog.setAttribute("aria-labelledby",title.id);const button=(icon,label,run)=>{const item=gridDecorateButton(gridButton("",run),icon,true);item.classList.add("icon");item.setAttribute("aria-label",labels[label]);return item};const step=position=>{paint(position);focusDrawerTitle(title)};const previous=button("up","gridPreviousRecord",()=>step(index-1)),next=button("down","gridNextRecord",()=>step(index+1));head.append(h("div"),previous,next,button("close","gridCloseRecord",()=>dialog.close()));head.firstChild.appendChild(title);dialog.append(head,body);let shown=loose,paints=null;const paint=position=>{if(!loose&&(position<0||position>=rows.length))return;if(!loose){index=position;shown=rows[index].h}
92
- const node=shown;paints?.abort();paints=new AbortController();setText(title,node.label);dialog._cxRecord=node;dialog.setAttribute("data-cx-record",node.id);dialog.setAttribute("data-cx-view",this.view.id);previous.disabled=!!loose||!index;next.disabled=!!loose||index===rows.length-1;this.paintDrawerBody(node,body,dialog,{index:loose?null:index,signal:paints.signal});dialog.dispatchEvent(new CustomEvent("cx-drawer-paint",{detail:{id:node.id,index:loose?null:index}}))};dialog._cxPaint=node=>{shown=node;paint(index)};dialog.addEventListener("keydown",event=>{if(event.defaultPrevented)return;if(event.key==="Escape"){event.preventDefault();dialog.close()}
93
- else if((event.key==="ArrowUp"||event.key==="ArrowDown")&&!event.target.matches("input,textarea,select,[role=tab]")&&!event.target.closest(".cx-data-grid,.cx-record-nested")){event.preventDefault();step(index +(event.key==="ArrowUp"?-1:1))}});const close=()=>dialog.close();owner.signal.addEventListener("abort",close,{once:true});dialog.addEventListener("close",()=>{owner.signal.removeEventListener("abort",close);paints?.abort();dialog.remove();if(TREE_DETAILS.active===dialog)TREE_DETAILS.active=null;if(this.recordDrawer!==dialog||this.destroyed)return;this.recordDrawer=null;if(TREE_DETAILS.active||dialog._replaced)return;const key=shown.id,row=this.rows.find(w=>w.key===key);if(returnFocus?.isConnected&&(!returnFocus.dataset.cxRecord||returnFocus.dataset.cxRecord===id))returnFocus.focus({preventScroll:true});else if(row)this.setFocus(row,true);else this.grid.focus({preventScroll:true})},{once:true});RECORD_IMPL.decorate?.(this,dialog,{head,identity:head.firstChild,title,body,options});document.body.appendChild(dialog);const phone=this.host.clientWidth<=640;dialog.classList.toggle("cx-phone",phone);if(phone)dialog.showModal();else dialog.show();paint(index);focusDrawerTitle(title);return dialog},paintDrawerBody(node,body,dialog,context){if(RECORD_IMPL.paint&&this.recordNeeded(node))return RECORD_IMPL.paint(this,node,body,dialog,context);const sections=new Map();body.replaceChildren();this.columns.forEach((column,position)=>{if(!position||column.drawer===false)return;const name=column.section||this.labels.gridDetailsSection;if(!sections.has(name)){const heading=h("div","cx-drawer-s"),list=h("dl","cx-dl");setText(heading,name);sections.set(name,list);body.append(heading,list)}
94
+ if(TREE_DETAILS.active){TREE_DETAILS.active._replaced=true;TREE_DETAILS.active._cxOverlay.close("replace",false,true);TREE_DETAILS.active.remove()}
95
+ const dialog=TREE_DETAILS.active=this.recordDrawer=h("dialog","cx-host cx-tree-tools cx-drawer"),head=h("div","cx-drawer-h"),title=h("h4"),body=h("div","cx-drawer-body");dialog._returnFocus=returnFocus;const restoreTheme=UI_TOOLS.theme(this.host,dialog);dialog.dataset.hfxPart="drawer";dialog.dataset.side="end";head.dataset.hfxPart="header";title.dataset.hfxPart="title";body.dataset.hfxPart="body";title.id=this.id+"-record-title";title.tabIndex=-1;title.setAttribute("aria-level","2");dialog.setAttribute("aria-labelledby",title.id);const button=(icon,label,run)=>{const item=gridDecorateButton(gridButton("",run),icon,true);item.classList.add("icon");item.setAttribute("aria-label",labels[label]);return item};const step=position=>{paint(position);focusDrawerTitle(title)};const previous=button("up","gridPreviousRecord",()=>step(index-1)),next=button("down","gridNextRecord",()=>step(index+1));head.append(h("div"),previous,next,button("close","gridCloseRecord",()=>dialog._cxOverlay.close()));head.firstChild.appendChild(title);dialog.append(head,body);let shown=loose,paints=null;const paint=position=>{if(!loose&&(position<0||position>=rows.length))return;if(!loose){index=position;shown=rows[index].h}
96
+ const node=shown;paints?.abort();paints=new AbortController();setText(title,node.label);dialog._cxRecord=node;dialog.setAttribute("data-cx-record",node.id);dialog.setAttribute("data-cx-view",this.view.id);previous.disabled=!!loose||!index;next.disabled=!!loose||index===rows.length-1;this.paintDrawerBody(node,body,dialog,{index:loose?null:index,signal:paints.signal});dialog.dispatchEvent(new CustomEvent("cx-drawer-paint",{detail:{id:node.id,index:loose?null:index}}))};dialog._cxPaint=node=>{shown=node;paint(index)};dialog.addEventListener("keydown",event=>{if(event.defaultPrevented)return;if((event.key==="ArrowUp"||event.key==="ArrowDown")&&!event.target.matches("input,textarea,select,[contenteditable],[role=tab]")&&!event.target.closest(".cx-data-grid,.cx-record-nested")){event.preventDefault();step(index +(event.key==="ArrowUp"?-1:1))}});const close=()=>dialog._cxOverlay.close("data",false,true);const lifetime=new AbortController();owner.signal.addEventListener("abort",close,{once:true});dialog.addEventListener("close",()=>{lifetime.abort();owner.signal.removeEventListener("abort",close);paints?.abort();dialog.remove();if(TREE_DETAILS.active===dialog)TREE_DETAILS.active=null;if(this.recordDrawer!==dialog||this.destroyed)return;this.recordDrawer=null;if(TREE_DETAILS.active||dialog._replaced)return;const key=shown.id,row=this.rows.find(w=>w.key===key);if(returnFocus?.isConnected&&(!returnFocus.dataset.cxRecord||returnFocus.dataset.cxRecord===id))returnFocus.focus({preventScroll:true});else if(row)this.setFocus(row,true);else this.grid.focus({preventScroll:true})},{once:true});RECORD_IMPL.decorate?.(this,dialog,{head,identity:head.firstChild,title,body,options});document.body.appendChild(dialog);const overlay=dialog._cxOverlay=UI_IMPL.drawer(dialog,{host:this.host,container:this.host,focus:false,dismissible:false},lifetime.signal);lifetime.signal.addEventListener("abort",restoreTheme,{once:true});paint(index);overlay.open();focusDrawerTitle(title);return dialog},paintDrawerBody(node,body,dialog,context){if(RECORD_IMPL.paint&&this.recordNeeded(node))return RECORD_IMPL.paint(this,node,body,dialog,context);const sections=new Map();body.replaceChildren();this.columns.forEach((column,position)=>{if(!position||column.drawer===false)return;const name=column.section||this.labels.gridDetailsSection;if(!sections.has(name)){const heading=h("div","cx-drawer-s"),list=h("dl","cx-dl");setText(heading,name);sections.set(name,list);body.append(heading,list)}
94
97
  const term=h("dt"),value=h("dd");setText(term,column.title);term.setAttribute("data-cx-field",column.id);value.setAttribute("data-cx-field",column.id);this.paintDrawerValue(column,node,value,position,context);sections.get(name).append(term,value)})},paintDrawerValue(column,node,value,position,context){const cell=this.cellOf(node,column,position),object=isCellObject(cell);if(typeof column.render==="function"){try{if(column.render(node,value,{column:column.id,level:1,expanded:false,index:position,signal:context.signal,onDispose:cleanup=>{if(context.signal.aborted)cleanup();else context.signal.addEventListener("abort",cleanup,{once:true})}})===true)return}
95
98
  catch(error){this.emit("error",{error})}}
96
99
  const text=cellText(cell,column,this.fmt),blank=object&&cell.value==null&&cell.text==="",span=h("span");applyTone(span,"cx-drawer-text" +(text||blank?"":" p-missing"),toneOf(columnValueTone(column,cell)||(object?cell.tone:null)));setText(span,text||blank?text:"—");if(object&&cell.title)span.title=cell.title;value.appendChild(span);const rule=columnNumericTone(column,cellValue(cell));if(rule){span.title=rule.label;const label=h("span","p-sub");setText(label,rule.label);value.appendChild(label)}
@@ -0,0 +1,41 @@
1
+ @layer hfx.components{
2
+ .cx-menu{
3
+ position:fixed;inset:auto;margin:var(--hfx-space-1-5,6px);padding:var(--hfx-space-1,0.25rem);
4
+ min-inline-size:min(var(--cx-ui-popup-min),calc(var(--cx-ui-viewport-inline) - var(--hfx-space-4,1rem)));
5
+ max-inline-size:min(var(--cx-ui-menu-max),calc(var(--cx-ui-viewport-inline) - var(--hfx-space-4,1rem)));
6
+ max-block-size:calc(var(--cx-ui-viewport-block) - var(--hfx-space-4,1rem));overflow:auto;
7
+ border:var(--hfx-border-width,1px) solid var(--hfx-color-border,light-dark(#e2e4e7,#272a30));border-radius:var(--hfx-radius-overlay,var(--hfx-radius-lg,10px));
8
+ background:var(--hfx-color-surface-raised,light-dark(#ffffff,#1e2126));color:var(--hfx-color-text,light-dark(#16181c,#f2f3f5));box-shadow:var(--hfx-elevation-2,0 1px 2px rgba(22,24,28,.05),0 6px 18px -10px rgba(22,24,28,.18));
9
+ font-family:var(--hfx-font-sans,"Segoe UI Variable Text","Segoe UI",system-ui,-apple-system,"Helvetica Neue",Arial,sans-serif);font-size:var(--hfx-control-font-size,calc(var(--hfx-type-body-size,0.875rem) - var(--cx-compact,0) * .0625rem));z-index:var(--hfx-z-popover,1000);
10
+ }
11
+ .cx-menu[hidden]{display:none!important;}
12
+ .cx-menu-item{
13
+ display:flex;align-items:center;inline-size:100%;min-block-size:var(--hfx-menu-item-height,calc(32px - var(--cx-compact,0) * 4px));
14
+ padding:0 var(--hfx-space-2,0.5rem);gap:var(--hfx-space-2,0.5rem);border:0;
15
+ border-radius:calc(var(--hfx-radius-overlay,var(--hfx-radius-lg,10px)) - var(--hfx-space-1,0.25rem));
16
+ background:transparent;color:inherit;font:inherit;text-align:start;cursor:pointer;
17
+ }
18
+ .cx-menu-item:is(:hover,:focus){background:var(--hfx-color-hover,light-dark(#f5f5f5,#232529));}
19
+ .cx-menu-item:active{background:var(--hfx-color-active,light-dark(#ebebec,#2e3034));}
20
+ .cx-menu-item:focus-visible{outline:var(--hfx-focus-width,2px) solid var(--hfx-color-focus-ring,light-dark(#02726a,#37c9b8));outline-offset:calc(-1 * var(--hfx-focus-width,2px));}
21
+ .cx-menu-label{flex:1;min-inline-size:0;overflow-wrap:anywhere;}
22
+ .cx-menu-icon{display:inline-flex;align-items:center;justify-content:center;flex:none;inline-size:var(--hfx-icon-md,16px);block-size:var(--hfx-icon-md,16px);color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));}
23
+ .cx-menu-icon > :is(svg,img){inline-size:100%;block-size:100%;}
24
+ .cx-menu-item[aria-checked="true"] .cx-menu-icon{color:var(--hfx-color-accent-text,light-dark(#02726a,#37c9b8));}
25
+ .cx-menu-item[aria-checked="true"] .cx-menu-icon > *{display:none;}
26
+ .cx-menu-item[aria-checked="true"] .cx-menu-icon{font-size:0;}
27
+ .cx-menu-item[aria-checked="true"] .cx-menu-icon::before{content:"✓" / "";font-size:var(--hfx-icon-md,16px);}
28
+ .cx-menu-chevron{flex:none;inline-size:var(--hfx-icon-sm,14px);color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));text-align:center;}
29
+ .cx-menu:dir(rtl) .cx-menu-chevron{transform:rotate(180deg);}
30
+ .cx-menu-item[data-danger="true"],.cx-menu-item[data-danger="true"] .cx-menu-icon{color:var(--hfx-color-status-bad-fg,light-dark(#b8292a,#f47171));}
31
+ .cx-menu-item[data-danger="true"]:is(:hover,:focus){background:var(--hfx-color-status-bad-bg,light-dark(#f9e4e4,#3c272a));}
32
+ .cx-menu-item[aria-disabled="true"],.cx-menu-item[aria-disabled="true"] .cx-menu-icon{color:var(--hfx-color-text-disabled,light-dark(#a3a8b0,#5a5f68));cursor:default;}
33
+ .cx-menu-item[aria-disabled="true"]:is(:hover,:focus,:active){background:transparent;}
34
+ .cx-menu-item kbd{flex:none;margin-inline-start:auto;padding:0 var(--hfx-space-1,0.25rem);border:var(--hfx-border-width,1px) solid var(--hfx-color-border,light-dark(#e2e4e7,#272a30));border-radius:var(--hfx-radius-sm,4px);background:var(--hfx-color-surface-sunken,light-dark(#f7f8f9,#111317));color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));font-family:var(--hfx-font-mono,"Cascadia Mono","Cascadia Code",Consolas,"SFMono-Regular",Menlo,monospace);font-size:var(--hfx-type-caption-size,0.75rem);line-height:var(--hfx-type-caption-line,1.35);}
35
+ .cx-menu-badge{flex:none;min-block-size:var(--hfx-badge-height,20px);padding:0 var(--hfx-space-1,0.25rem);border-radius:var(--hfx-radius-badge,var(--hfx-radius-sm,4px));background:var(--hfx-color-accent-subtle,light-dark(#e7f1f1,#1b3435));color:var(--hfx-color-accent-text,light-dark(#02726a,#37c9b8));font-size:var(--hfx-type-caption-size,0.75rem);line-height:var(--hfx-type-caption-line,1.35);}
36
+ .cx-menu-group-label{padding:var(--hfx-space-1-5,6px) var(--hfx-space-2,0.5rem) var(--hfx-space-1,0.25rem);color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));font-size:var(--hfx-type-caption-size,0.75rem);font-weight:var(--hfx-weight-semibold,600);text-transform:uppercase;}
37
+ .cx-menu-separator{margin:var(--hfx-space-1,0.25rem) calc(-1 * var(--hfx-space-1,0.25rem));border-block-start:var(--hfx-border-width,1px) solid var(--hfx-color-border,light-dark(#e2e4e7,#272a30));}
38
+ .cx-context-sheet > .cx-menu{position:static;margin:0;min-inline-size:0;max-inline-size:none;max-block-size:var(--cx-ui-sheet-max);inline-size:100%;border:0;box-shadow:none;}
39
+ @media print{.cx-menu,.cx-context-sheet{display:none!important;}}
40
+ @media (forced-colors:active){.cx-menu-item:focus{outline:var(--hfx-focus-width,2px) solid;outline-offset:calc(-1 * var(--hfx-focus-width,2px));}}
41
+ }