@evotecit/canopyx 0.3.15 → 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 (118) 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 +2 -0
  24. package/CanopyX.Html/Assets/canopyx-flow-checks.js +5 -4
  25. package/CanopyX.Html/Assets/canopyx-flow-dialog.css +2 -0
  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 +5 -5
  29. package/CanopyX.Html/Assets/canopyx-flow-facts.js +2 -0
  30. package/CanopyX.Html/Assets/canopyx-flow-module.mjs +5 -5
  31. package/CanopyX.Html/Assets/canopyx-flow.js +8 -7
  32. package/CanopyX.Html/Assets/canopyx-format-editor.js +2 -2
  33. package/CanopyX.Html/Assets/canopyx-formatting.js +10 -9
  34. package/CanopyX.Html/Assets/canopyx-grid-module.mjs +185 -131
  35. package/CanopyX.Html/Assets/canopyx-grid.css +228 -96
  36. package/CanopyX.Html/Assets/canopyx-grid.js +185 -131
  37. package/CanopyX.Html/Assets/canopyx-grouping.css +22 -15
  38. package/CanopyX.Html/Assets/canopyx-grouping.js +24 -17
  39. package/CanopyX.Html/Assets/canopyx-interactive.css +197 -74
  40. package/CanopyX.Html/Assets/canopyx-interactive.js +38 -45
  41. package/CanopyX.Html/Assets/canopyx-labels-de.js +177 -9
  42. package/CanopyX.Html/Assets/canopyx-labels-es.js +177 -9
  43. package/CanopyX.Html/Assets/canopyx-labels-fr.js +176 -8
  44. package/CanopyX.Html/Assets/canopyx-labels-it.js +176 -8
  45. package/CanopyX.Html/Assets/canopyx-labels-pl.js +176 -8
  46. package/CanopyX.Html/Assets/canopyx-layouts.css +48 -20
  47. package/CanopyX.Html/Assets/canopyx-layouts.js +36 -14
  48. package/CanopyX.Html/Assets/canopyx-link.js +2 -2
  49. package/CanopyX.Html/Assets/canopyx-live.css +1 -3
  50. package/CanopyX.Html/Assets/canopyx-live.js +20 -15
  51. package/CanopyX.Html/Assets/canopyx-matrix.css +1 -8
  52. package/CanopyX.Html/Assets/canopyx-matrix.js +11 -11
  53. package/CanopyX.Html/Assets/canopyx-module.mjs +38 -45
  54. package/CanopyX.Html/Assets/canopyx-office-export.js +2936 -0
  55. package/CanopyX.Html/Assets/canopyx-path.css +45 -0
  56. package/CanopyX.Html/Assets/canopyx-path.js +28 -0
  57. package/CanopyX.Html/Assets/canopyx-pivot.css +75 -0
  58. package/CanopyX.Html/Assets/canopyx-pivot.js +72 -0
  59. package/CanopyX.Html/Assets/canopyx-plan.css +56 -0
  60. package/CanopyX.Html/Assets/canopyx-plan.js +67 -0
  61. package/CanopyX.Html/Assets/canopyx-presenters.js +2 -2
  62. package/CanopyX.Html/Assets/canopyx-record-panel.css +11 -2
  63. package/CanopyX.Html/Assets/canopyx-record-panel.js +18 -12
  64. package/CanopyX.Html/Assets/canopyx-reporting-module.mjs +225 -164
  65. package/CanopyX.Html/Assets/canopyx-reporting.css +240 -107
  66. package/CanopyX.Html/Assets/canopyx-reporting.js +225 -164
  67. package/CanopyX.Html/Assets/canopyx-stack.css +35 -0
  68. package/CanopyX.Html/Assets/canopyx-stack.js +24 -0
  69. package/CanopyX.Html/Assets/canopyx-time-slider.css +18 -0
  70. package/CanopyX.Html/Assets/canopyx-time-slider.js +27 -0
  71. package/CanopyX.Html/Assets/canopyx-timeline.js +6 -5
  72. package/CanopyX.Html/Assets/canopyx-tree-tools.css +36 -35
  73. package/CanopyX.Html/Assets/canopyx-tree-tools.js +15 -12
  74. package/CanopyX.Html/Assets/canopyx-ui-menu.css +41 -0
  75. package/CanopyX.Html/Assets/canopyx-ui-menu.js +64 -0
  76. package/CanopyX.Html/Assets/canopyx-ui-navigation.css +73 -0
  77. package/CanopyX.Html/Assets/canopyx-ui-navigation.js +39 -0
  78. package/CanopyX.Html/Assets/canopyx-ui-overlays.css +79 -0
  79. package/CanopyX.Html/Assets/canopyx-ui-overlays.js +42 -0
  80. package/CanopyX.Html/Assets/canopyx-ui-picker.css +12 -0
  81. package/CanopyX.Html/Assets/canopyx-ui-picker.js +6 -0
  82. package/CanopyX.Html/Assets/canopyx-ui-stepper.css +174 -0
  83. package/CanopyX.Html/Assets/canopyx-ui-stepper.js +48 -0
  84. package/CanopyX.Html/Assets/canopyx-ui-tabs.css +153 -0
  85. package/CanopyX.Html/Assets/canopyx-ui-tabs.js +47 -0
  86. package/CanopyX.Html/Assets/canopyx-values.css +5 -0
  87. package/CanopyX.Html/Assets/canopyx-values.js +13 -0
  88. package/CanopyX.Html/Assets/canopyx-view-intents.css +12 -0
  89. package/CanopyX.Html/Assets/canopyx-view-intents.js +56 -0
  90. package/CanopyX.Html/Assets/canopyx.css +197 -71
  91. package/CanopyX.Html/Assets/canopyx.js +29 -19
  92. package/JavaScript/board.d.ts +30 -0
  93. package/JavaScript/changes.d.ts +4 -1
  94. package/JavaScript/facets.d.ts +5 -3
  95. package/JavaScript/flow.d.ts +6 -0
  96. package/JavaScript/grid.d.ts +153 -17
  97. package/JavaScript/hosts.d.ts +14 -1
  98. package/JavaScript/index.d.ts +4 -2
  99. package/JavaScript/live.d.ts +2 -0
  100. package/JavaScript/locales/de.mjs +177 -9
  101. package/JavaScript/locales/es.mjs +177 -9
  102. package/JavaScript/locales/fr.mjs +176 -8
  103. package/JavaScript/locales/it.mjs +176 -8
  104. package/JavaScript/locales/pl.mjs +176 -8
  105. package/JavaScript/matrix.d.ts +4 -1
  106. package/JavaScript/path.d.ts +33 -0
  107. package/JavaScript/pivot.d.ts +135 -0
  108. package/JavaScript/plan.d.ts +23 -0
  109. package/JavaScript/protocol.d.ts +16 -3
  110. package/JavaScript/records.d.ts +20 -4
  111. package/JavaScript/stack.d.ts +28 -0
  112. package/JavaScript/time-slider.d.ts +58 -0
  113. package/JavaScript/ui.d.ts +261 -0
  114. package/JavaScript/view-intents.d.ts +45 -0
  115. package/README.md +22 -3
  116. package/ThirdParty/OfficeIMO/LICENSE +21 -0
  117. package/ThirdParty/OfficeIMO/README.md +13 -0
  118. package/package.json +76 -3
@@ -0,0 +1,45 @@
1
+ .cx-path-host > .cx-grid{display:none;}
2
+ .cx-path{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);min-width:0;min-height:0;flex:1 1 auto;overflow:auto;padding:var(--hfx-space-4,1rem);}
3
+ .cx-path-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-3,0.75rem);}
4
+ .cx-path-help,.cx-path-empty{margin:var(--hfx-space-2,0.5rem) 0 var(--hfx-space-4,1rem);color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));font-size:var(--hfx-type-caption-size,0.75rem);line-height:var(--hfx-type-body-line,1.5);}
5
+ .cx-path-chain{min-width:0;margin-bottom:var(--hfx-space-6,2rem);}
6
+ .cx-path-heading{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);font-size:var(--hfx-type-body-size,0.875rem);font-weight:var(--hfx-weight-semibold,600);margin:0 0 calc(var(--hfx-space-1,0.25rem)*2.5);overflow-wrap:anywhere;}
7
+ .cx-path-hops{display:flex;align-items:stretch;gap:calc(var(--hfx-space-1,0.25rem)*7);margin:0;padding:var(--hfx-space-0-5,2px) var(--hfx-space-0-5,2px) var(--hfx-space-3,0.75rem);list-style:none;overflow-x:auto;}
8
+ .cx-path-hop{position:relative;flex:1 0 calc(var(--hfx-space-1,0.25rem)*50);max-width:calc(var(--hfx-space-1,0.25rem)*70);min-width:0;padding:var(--hfx-space-3,0.75rem);box-sizing:border-box;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));background:var(--hfx-color-surface,light-dark(#ffffff,#16181c));overflow-wrap:anywhere;}
9
+ .cx-path-hop + .cx-path-hop::before{content:"";position:absolute;inset-inline-start:calc(-1 * var(--hfx-space-6,2rem));top:var(--hfx-space-6,2rem);inline-size:var(--hfx-space-5,1.5rem);border-block-start:calc(2 * var(--hfx-border-width,1px)) solid var(--hfx-color-accent,light-dark(#11776f,#37c9b8));}
10
+ .cx-path-hop + .cx-path-hop::after{content:"";position:absolute;inset-inline-start:calc(-1 * var(--hfx-space-3,0.75rem));top:calc(var(--hfx-space-6,2rem) - var(--hfx-space-1,0.25rem));inline-size:var(--hfx-space-2,0.5rem);block-size:var(--hfx-space-2,0.5rem);border-block-start:calc(2 * var(--hfx-border-width,1px)) solid var(--hfx-color-accent,light-dark(#11776f,#37c9b8));border-inline-end:calc(2 * var(--hfx-border-width,1px)) solid var(--hfx-color-accent,light-dark(#11776f,#37c9b8));transform:rotate(45deg);}
11
+ .cx-path-step{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hfx-space-1,0.25rem) var(--hfx-space-2,0.5rem);margin-bottom:var(--hfx-space-2,0.5rem);color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));font-size:var(--hfx-type-caption-size,0.75rem);}
12
+ .cx-path-endpoint{font-weight:var(--hfx-weight-semibold,600);}
13
+ .cx-path-title{display:block;box-sizing:border-box;width:100%;height:auto;min-height:var(--hfx-control-height-lg,calc(44px - var(--cx-compact,0) * 8px));text-align:start;white-space:normal;overflow-wrap:anywhere;}
14
+ .cx-path-label{margin:var(--hfx-space-2,0.5rem) 0;font-size:var(--hfx-type-caption-size,0.75rem);line-height:var(--hfx-type-body-line,1.5);}
15
+ .cx-path-tokens{display:flex;flex-wrap:wrap;align-items:start;gap:var(--hfx-space-1-5,6px);margin-top:var(--hfx-space-2,0.5rem);}
16
+ .cx-path-tokens > *{margin:0;white-space:normal;overflow-wrap:anywhere;}
17
+ .cx-phone .cx-path-hops{flex-direction:column;overflow:visible;gap:var(--hfx-space-6,2rem);padding:var(--hfx-space-0-5,2px);}
18
+ .cx-phone .cx-path-hop{flex:none;max-width:none;width:100%;}
19
+ @media (max-width: 640px){
20
+ .cx-path-hops{flex-direction:column;overflow:visible;gap:var(--hfx-space-6,2rem);padding:var(--hfx-space-0-5,2px);}
21
+ .cx-path-hop{flex:none;max-width:none;width:100%;}
22
+ }
23
+ @media print{
24
+ .cx-path-host{height:auto !important;max-height:none !important;overflow:visible !important;}
25
+ .cx-path{overflow:visible;height:auto;max-height:none;flex:none;}
26
+ .cx-path-help{display:none;}
27
+ .cx-path-hops{display:block;overflow:visible;padding:0 0 0 calc(var(--hfx-space-1,0.25rem)*7);list-style:decimal;}
28
+ .cx-path-hop,.cx-phone .cx-path-hop{max-width:none;width:auto;margin:0 0 calc(var(--hfx-space-1,0.25rem)*2.5);break-inside:avoid;}
29
+ .cx-path-hop + .cx-path-hop::before{content:none;}
30
+ .cx-path-title{display:block;box-sizing:border-box;width:100%;height:auto;min-height:var(--hfx-control-height-lg,calc(44px - var(--cx-compact,0) * 8px));text-align:start;white-space:normal;overflow-wrap:anywhere;}
31
+ }
32
+ .cx-path .cx-path-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;}
33
+ .cx-path .cx-path-title:hover{color:var(--hfx-color-text,light-dark(#16181c,#f2f3f5));background:transparent;text-decoration:underline;}
34
+ .cx-path .cx-path-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);}
35
+ .cx-path-endpoint{padding:var(--hfx-space-0-5,2px) var(--hfx-space-1-5,6px);border:var(--hfx-border-width,1px) solid var(--hfx-color-border,light-dark(#e2e4e7,#272a30));border-radius:var(--hfx-radius-status,999px);color:var(--hfx-color-text,light-dark(#16181c,#f2f3f5));background:var(--hfx-color-surface-sunken,light-dark(#f7f8f9,#111317));}
36
+ .cx-path-disabled{background:var(--hfx-color-surface-sunken,light-dark(#f7f8f9,#111317));border-style:dashed;}
37
+ .cx-path-disabled :is(.cx-path-title,.cx-path-label){color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));}
38
+ .cx-path-return{display:flex;align-items:center;gap:var(--hfx-space-2,0.5rem);color:var(--hfx-color-accent,light-dark(#11776f,#37c9b8));font-size:var(--hfx-type-caption-size,0.75rem);margin-block-start:var(--hfx-space-3,0.75rem);padding-block:var(--hfx-space-1,0.25rem);}
39
+ .cx-path-return svg{inline-size:var(--hfx-space-6,2rem);block-size:var(--hfx-space-6,2rem);fill:none;stroke:currentColor;stroke-width:2;}
40
+ @media(max-width:640px){
41
+ .cx-path-hop + .cx-path-hop::before{inset-inline-start:var(--hfx-space-5,1.5rem);top:calc(-1 * var(--hfx-space-6,2rem));inline-size:0;block-size:var(--hfx-space-5,1.5rem);border-block-start:0;border-inline-start:calc(2 * var(--hfx-border-width,1px)) solid var(--hfx-color-accent,light-dark(#11776f,#37c9b8));}
42
+ .cx-path-hop + .cx-path-hop::after{inset-inline-start:calc(var(--hfx-space-5,1.5rem) - var(--hfx-space-1,0.25rem));top:calc(-1 * var(--hfx-space-3,0.75rem));transform:rotate(135deg);}
43
+ }
44
+ .cx-phone .cx-path-hop + .cx-path-hop::before{inset-inline-start:var(--hfx-space-5,1.5rem);top:calc(-1 * var(--hfx-space-6,2rem));inline-size:0;block-size:var(--hfx-space-5,1.5rem);border-block-start:0;border-inline-start:calc(2 * var(--hfx-border-width,1px)) solid var(--hfx-color-accent,light-dark(#11776f,#37c9b8));}
45
+ .cx-phone .cx-path-hop + .cx-path-hop::after{inset-inline-start:calc(var(--hfx-space-5,1.5rem) - var(--hfx-space-1,0.25rem));top:calc(-1 * var(--hfx-space-3,0.75rem));transform:rotate(135deg);}
@@ -0,0 +1,28 @@
1
+ (function(global){"use strict";const installed=new WeakSet();const part={version:"28b4b60b1851bf2e",install(context){if(installed.has(context.DataGrid.prototype))return;const{DataGrid,h,setText,fill,cellValue,cellText,badgeElement,applyTone,toneOf,PROTOCOL}=context;const PATH_TEXT={pathEmpty:"No paths to show",pathSource:"Source",pathTarget:"Target",pathStep:"Step {number} of {total}",pathCycle:"Cycle",pathForeign:"Foreign",pathDisabled:"Disabled",pathKeys:"Use Tab or arrow keys to browse steps; Enter or Space opens the record.",pathPartial:"Showing {shown} of {total} steps."};const PATH_FLAGS={cycle:"warning",foreign:"info",disabled:"muted"};function pathKeys(value,allowed){if(Object.keys(value).some(key=>!allowed.includes(key)))throw new Error("CanopyX: path settings have an unknown property.")}
2
+ function pathText(value,maximum=Infinity){if(typeof value!=="string"||!value.trim()||value.length>maximum||/[\u0000-\u001f\u007f-\u009f]/.test(value)) return false;
3
+ for(let at=0;at<value.length;at++){const code=value.charCodeAt(at);if(code>=0xd800&&code<=0xdbff){const next=value.charCodeAt(++at);if(!(next>=0xdc00&&next<=0xdfff))return false}
4
+ else if(code>=0xdc00&&code<=0xdfff)return false}
5
+ return true}
6
+ function pathSettings(grid){grid.validatePathSource();const input=grid.view.path,object=value=>value&&typeof value==="object"&&!Array.isArray(value);const known=id=>pathText(id)&&grid.columns.some(column=>column.id===id);if(grid.view.protocol!==PROTOCOL||!object(input)||input.titleColumn!==undefined&&!known(input.titleColumn)||
7
+ !Array.isArray(input.chains)||input.chains.length>200)
8
+ throw new Error("CanopyX: a path needs inline records with stable identities, a known title column and at most 200 chains.");pathKeys(input,["titleColumn","chains"]);const rows=grid.view.rows;if(!Array.isArray(rows))throw new Error("CanopyX: path records are not available.");const records=new Map(),ids=new Set();let total=0;for(const row of rows){if(!object(row)||!pathText(row.id)||records.has(row.id))throw new Error("CanopyX: path records need distinct stable identities.");records.set(row.id,row)}
9
+ for(const chain of input.chains){if(!object(chain)||!pathText(chain.id,256)||ids.has(chain.id)||!pathText(chain.label,4096)||
10
+ !Array.isArray(chain.hops)||chain.hops.length<1||chain.hops.length>256)
11
+ throw new Error("CanopyX: path chains need unique identities, labels and one to 256 steps.");pathKeys(chain,["id","label","hops"]);ids.add(chain.id);total +=chain.hops.length;if(total>10000)throw new Error("CanopyX: a path has at most 10,000 steps in total.");for(const hop of chain.hops){if(!object(hop)||!pathText(hop.record)||!records.has(hop.record)||hop.label!==undefined&&!pathText(hop.label,4096)||
12
+ hop.flags!==undefined&&(!Array.isArray(hop.flags)||hop.flags.length>3||new Set(hop.flags).size!==hop.flags.length||
13
+ hop.flags.some(flag=>typeof flag!=="string"||!Object.hasOwn(PATH_FLAGS,flag)))||
14
+ hop.badges!==undefined&&(!Array.isArray(hop.badges)||hop.badges.length>16||hop.badges.some(badge=>!object(badge)||
15
+ !pathText(badge.text,256)||badge.title!==undefined&&!pathText(badge.title,4096)||
16
+ badge.tone!==undefined&&(typeof badge.tone!=="string"||!/^[a-z][a-z0-9-]{0,31}$/.test(badge.tone) || badge.tone.endsWith("-bg")))))
17
+ throw new Error("CanopyX: path steps need existing record references and valid labels, distinct flags and badges.");pathKeys(hop,["record","label","flags","badges"]);for(const badge of hop.badges||[])pathKeys(badge,["text","tone","title"])}}
18
+ const titleColumn=input.titleColumn||grid.columns.find(column=>column.rowHeader)?.id||grid.columns.find(column=>!column.hidden)?.id;return{...input,titleColumn,records,total}}
19
+ Object.assign(DataGrid.prototype,{validatePathSource(){if(this.view.source.kind!=="inline"||this.opts.source||this.view.rowIdentity==="ordinal")
20
+ throw new Error("CanopyX: a path requires stable inline records.")},async startPath(){if(this.path)return;if(this.view.rowData)await this.source.decoded;if(this.destroyed)return;const settings=pathSettings(this),labels={};for(const[key,text]of Object.entries(PATH_TEXT))labels[key]=this.opts.labels?.[key]||this.view.labels?.[key]||text;this.path={settings,labels,rows:[...settings.records.values()]};const hidden=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-path-host");this.grid.hidden=true;if(this.pager)this.pager.hidden=true;this.pathFrame=h("section","cx-path");this.pathFrame.setAttribute("aria-label",this.view.label);this.host.insertBefore(this.pathFrame,this.grid.nextSibling);this.pathPaint();const repaint=()=>{try{this.path.settings=pathSettings(this);this.path.rows=[...this.path.settings.records.values()];if(this.path.failure){this.path.failure=false;this.setNotice("",false)}
21
+ this.pathPaint()}catch(error){this.path.rows=[];this.path.settings={...this.path.settings,records:new Map()};this.path.failure=true;this.pathFrame.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.pathFrame.remove();this.host.classList.remove("cx-path-host");this.grid.hidden=hidden;this.toolbar.hidden=toolbarHidden;this.host.style.maxHeight=originalMaxHeight;if(this.pager)this.pager.hidden=pagerHidden;this.path=null},{once:true})}});function pathFrameTemplate(settings,labels,appendHop){const title=h("h2","cx-path-title-heading");setText(title,this.view.label);this.pathFrame.appendChild(title);const help=h("p","cx-path-help");help.id=this.id+"-path-help";setText(help,labels.pathKeys);this.pathFrame.appendChild(help);if(!settings.chains.length){const empty=h("p","cx-path-empty");setText(empty,labels.pathEmpty);this.pathFrame.appendChild(empty)}
22
+ for(const[chainIndex,chain]of settings.chains.entries()){const section=h("section","cx-path-chain"),heading=h("h3","cx-path-heading"),hops=h("ol","cx-path-hops");hops.setAttribute("role","list");heading.id=this.id+"-path-chain-"+chainIndex;setText(heading,chain.label);section.dataset.cxPathChain=chain.id;section.setAttribute("aria-labelledby",heading.id);section.append(heading,hops);this.pathFrame.appendChild(section);for(const[index,hop]of chain.hops.entries()){const item=appendHop(hop,index,chainIndex,chain.hops.length);hops.appendChild(item);if((hop.flags||[]).includes("cycle")){const target=chain.hops.findIndex((previous,at)=>at<index&&previous.record===hop.record),step=target<0?0:target;const link=h("a","cx-path-return"),icon=document.createElementNS("http://www.w3.org/2000/svg","svg");link.href="#"+this.id+"-path-"+chainIndex+"-"+step+"-title";icon.setAttribute("viewBox","0 0 24 24");icon.setAttribute("aria-hidden","true");const route=document.createElementNS(icon.namespaceURI,"path");route.setAttribute("d","M20 3V13Q20 20 13 20H4M9 15L4 20L9 24");icon.appendChild(route);link.append(icon,document.createTextNode(labels.pathCycle+": "+labels.pathSource+" · "+fill(labels.pathStep,{number:step+1,total:chain.hops.length})));link.addEventListener("click",event=>{event.preventDefault();document.getElementById(link.hash.slice(1))?.focus()});item.appendChild(link)}}}}
23
+ function pathHopTemplate(hop,row,labels,text,badges,index,chainIndex,total){const item=h("li","cx-path-hop"),step=h("div","cx-path-step"),title=h("button","cx-btn cx-path-title"),notes=h("div","cx-path-notes");const prefix=this.id+"-path-"+chainIndex+"-"+index,ordinal=h("span");step.id=prefix+"-step";notes.id=prefix+"-notes";setText(ordinal,fill(labels.pathStep,{number:index+1,total}));step.appendChild(ordinal);for(const label of[index===0?labels.pathSource:null,index===total-1?labels.pathTarget:null].filter(Boolean)){const marker=h("span","cx-path-endpoint");setText(marker,label);step.appendChild(marker)}
24
+ title.id=prefix+"-title";item.classList.toggle("cx-path-disabled",(hop.flags||[]).includes("disabled"));title.type="button";setText(title,text);title.dataset.cxPathRecord=row.id;title.dataset.cxPathPosition=chainIndex+":"+index;title.setAttribute("aria-describedby",step.id+" "+notes.id);item.append(step,title,notes);if(hop.label){const label=h("p","cx-path-label");setText(label,hop.label);notes.appendChild(label)}
25
+ const tokens=h("div","cx-path-tokens");for(const flag of hop.flags||[]){const token=badgeElement({text:labels["path"+flag[0].toUpperCase()+ flag.slice(1)],tone:PATH_FLAGS[flag]});token.dataset.cxPathFlag=flag;tokens.appendChild(token)}
26
+ for(const badge of badges)tokens.appendChild(badgeElement(badge));if(tokens.childNodes.length)notes.appendChild(tokens);return{item,title}}
27
+ Object.assign(DataGrid.prototype,{pathPaint(){const path=this.path,settings=path.settings,labels=path.labels;const active=document.activeElement,held=this.pathFrame.contains(active)?active.dataset.cxPathPosition:null;this.pathFrame.replaceChildren();pathFrameTemplate.call(this,settings,labels,(hop,index,chainIndex,total)=>this.pathHop(hop,index,chainIndex,total));if(held)[...this.pathFrame.querySelectorAll("[data-cx-path-position]")].find(node=>node.dataset.cxPathPosition===held)?.focus({preventScroll:true})},pathHop(hop,index,chainIndex,total){const path=this.path,labels=path.labels,row=path.settings.records.get(hop.record);const column=this.columns.find(column=>column.id===path.settings.titleColumn),cell=row.cells[column.id];const text=cellText(cell,column,this.fmt)||row.id;const badges=[...(cell&&typeof cell==="object"?cell.badges||[]:[]),...(hop.badges||[])];const{item,title}=pathHopTemplate.call(this,hop,row,labels,text,badges,index,chainIndex,total);title.addEventListener("click",()=>this.openRecord(row.id,{returnFocus:title}).catch(error=>this.emit("error",{error})));title.addEventListener("keydown",event=>{if(event.altKey||event.ctrlKey||event.metaKey||event.shiftKey)return;const buttons=[...item.parentElement.querySelectorAll(".cx-path-title")];const target=event.key==="Home"?buttons[0]:event.key==="End"?buttons.at(-1):
28
+ ["ArrowLeft","ArrowUp"].includes(event.key)?buttons[index-1]:["ArrowRight","ArrowDown"].includes(event.key)?buttons[index+1]:null;if(target){event.preventDefault();target.focus()}});return item}});installed.add(context.DataGrid.prototype)}};(global.CanopyXParts||=[]).push(["path",part])})(typeof window!=="undefined"?window:globalThis);
@@ -0,0 +1,75 @@
1
+ .cx-pivot-active > :is(.cx-grid,.cx-groupbar,.cx-grid-pager,.cx-grid-status,.cx-grid-summary){display:none !important;}
2
+ .cx-host.cx-pivot-active{min-height:0;}
3
+ body.cx-page > .cx-host.cx-pivot-active,body.cx-page > .cx-main > .cx-host.cx-pivot-active{height:auto;max-height:100dvh;min-height:0;}
4
+ .cx-pivot{display:flex;flex:1 1 auto;min-height:0;flex-direction:column;overflow:hidden;background:var(--hfx-color-surface,light-dark(#ffffff,#16181c));color:var(--hfx-color-text,light-dark(#16181c,#f2f3f5));}
5
+ .cx-pivot[hidden]{display:none;}
6
+ .cx-pivot-zones{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hfx-space-2,0.5rem);padding:calc(var(--hfx-space-1,0.25rem)*2.5) var(--hfx-space-3,0.75rem);border-bottom:var(--hfx-border-width,1px) solid var(--hfx-color-border,light-dark(#e2e4e7,#272a30));}
7
+ .cx-pivot-zone{display:flex;align-items:center;gap:var(--hfx-space-1-5,6px);min-width:0;max-width:100%;}
8
+ .cx-pivot-zone > span:first-child{color:var(--hfx-color-text-secondary,light-dark(#4d525b,#b0b4bc));font-size:var(--hfx-type-caption-size,0.75rem);}
9
+ .cx-pivot-zone .cx-gchip{block-size:var(--hfx-control-height-sm,calc(28px - var(--cx-compact,0) * 4px));max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
10
+ .cx-pivot-zone .cx-gchip-name{overflow:hidden;text-overflow:ellipsis;}
11
+ .cx-pivot-scroll{min-height:0;min-width:0;overflow:auto;flex:1 1 auto;}
12
+ .cx-pivot-table{border-collapse:separate;border-spacing:0;font-size:var(--hfx-type-small-size,0.8125rem);width:max-content;min-width:100%;font-variant-numeric:tabular-nums;}
13
+ .cx-pivot-table :is(th,td){padding:calc(var(--hfx-space-1,0.25rem)*1.75) calc(var(--hfx-space-1,0.25rem)*2.5);border-bottom:var(--hfx-border-width,1px) solid var(--hfx-color-border,light-dark(#e2e4e7,#272a30));border-right:var(--hfx-border-width,1px) solid var(--hfx-color-border,light-dark(#e2e4e7,#272a30));vertical-align:middle;}
14
+ .cx-pivot-table thead th{--cx-pivot-header-layer:1;background:var(--hfx-table-header-background,var(--hfx-color-surface,light-dark(#ffffff,#16181c)));color:var(--hfx-color-text-secondary,light-dark(#4d525b,#b0b4bc));text-align:right;position:sticky;top:0;z-index:var(--cx-pivot-header-layer);}
15
+ .cx-pivot-table thead th > span{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
16
+ .cx-pivot-table th[scope=row]{text-align:left;white-space:nowrap;padding-left:calc(var(--hfx-space-3,0.75rem) + var(--cx-pivot-depth,0) * var(--hfx-space-6,2rem));position:sticky;left:0;background:var(--hfx-color-surface,light-dark(#ffffff,#16181c));}
17
+ .cx-pivot-table td{text-align:right;}
18
+ .cx-pivot-table tbody tr:hover{background:color-mix(in srgb,var(--hfx-color-accent,light-dark(#11776f,#37c9b8)) 5%,var(--hfx-color-surface,light-dark(#ffffff,#16181c)));}
19
+ .cx-pivot-subtotal{font-weight:var(--hfx-weight-semibold,600);background:color-mix(in srgb,var(--hfx-color-text,light-dark(#16181c,#f2f3f5)) 3%,var(--hfx-color-surface,light-dark(#ffffff,#16181c)));}
20
+ .cx-pivot-table tfoot{font-weight:var(--hfx-weight-display,600);position:sticky;bottom:0;background:var(--hfx-color-surface,light-dark(#ffffff,#16181c));}
21
+ .cx-pivot-cell,.cx-pivot-value-menu{border:0;background:transparent;color:inherit;font:inherit;cursor:pointer;padding:var(--hfx-space-0-5,2px) 0;}
22
+ .cx-pivot-cell{width:100%;text-align:right;min-width:72px;}
23
+ .cx-pivot-cell:hover,.cx-pivot-value-menu:hover{color:var(--hfx-color-accent,light-dark(#11776f,#37c9b8));text-decoration:underline;}
24
+ .cx-pivot-cell:focus-visible,.cx-pivot-value-menu:focus-visible{outline:calc(var(--hfx-border-width,1px)*2) solid var(--hfx-color-accent,light-dark(#11776f,#37c9b8));outline-offset:calc(var(--hfx-border-width,1px)*2);}
25
+ .cx-pivot-value-menu{display:block;margin:var(--hfx-space-0-5,2px) 0 0 auto;white-space:nowrap;}
26
+ .cx-pivot-collapse{padding:0 var(--hfx-space-1,0.25rem);min-width:var(--hfx-space-5,1.5rem);margin-right:var(--hfx-space-1,0.25rem);}
27
+ .cx-pivot-caption{font-size:calc(var(--hfx-type-caption-size,0.75rem)*.9167);font-weight:normal;color:var(--hfx-color-text-secondary,light-dark(#4d525b,#b0b4bc));}
28
+ .cx-pivot-note{margin:0;padding:var(--hfx-space-2,0.5rem) var(--hfx-space-3,0.75rem);color:var(--hfx-color-text-secondary,light-dark(#4d525b,#b0b4bc));font-size:var(--hfx-type-caption-size,0.75rem);}
29
+ .cx-pivot-editor{display:grid;gap:var(--hfx-space-3,0.75rem);padding:0;min-height:0;}
30
+ .cx-pivot-editor fieldset{min-width:0;border:0;padding:0;margin:0;}
31
+ .cx-pivot-editor label{display:flex;gap:var(--hfx-space-2,0.5rem);align-items:center;font-size:var(--hfx-type-small-size,0.8125rem);margin:calc(var(--hfx-space-1,0.25rem)*1.25) 0;}
32
+ .cx-pivot-editor :is(select,input){min-width:0;}
33
+ .cx-pivot-editor .cx-pivot-setting{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--hfx-space-2,0.5rem);}
34
+ .cx-pivot-setting > :is(span,input){grid-column:1/-1;}
35
+ .cx-pivot-return{padding:var(--hfx-space-2,0.5rem) var(--hfx-space-3,0.75rem);border-bottom:var(--hfx-border-width,1px) solid var(--hfx-color-border,light-dark(#e2e4e7,#272a30));}
36
+ @media (max-width:640px){
37
+ .cx-pivot-zones{gap:var(--hfx-space-1-5,6px);padding:var(--hfx-space-2,0.5rem);}
38
+ .cx-pivot-zone{width:100%;}
39
+ .cx-pivot-zone .cx-gchip{max-width:180px;}
40
+ .cx-pivot-table :is(th,td){padding:calc(var(--hfx-space-1,0.25rem)*1.75);}
41
+ }
42
+ @media print{
43
+ .cx-host.cx-pivot-active{max-height:none !important;overflow:visible !important;}
44
+ .cx-pivot{display:block;overflow:visible;}
45
+ .cx-pivot-scroll{overflow:visible;}
46
+ .cx-pivot-table{font-size:calc(var(--hfx-type-caption-size,0.75rem)*.8333);}
47
+ .cx-pivot-table thead{display:table-header-group;}
48
+ .cx-pivot-table tr{break-inside:avoid;}
49
+ .cx-pivot-table button{display:contents;}
50
+ .cx-pivot-table :is(thead th,tfoot){position:static;}
51
+ .cx-pivot-zones,.cx-pivot-picker,.cx-pivot-return,.cx-pivot-collapse{display:none !important;}
52
+ }
53
+ .cx-pivot-table tbody th{font-weight:var(--hfx-weight-regular,400);}
54
+ .cx-pivot-table .cx-pivot-subtotal > :is(th,td){background:var(--hfx-color-surface-sunken,light-dark(#f7f8f9,#111317));font-weight:var(--hfx-weight-semibold,600);}
55
+ .cx-pivot-table tfoot .cx-pivot-subtotal > :is(th,td){background:var(--hfx-color-surface-sunken,light-dark(#f7f8f9,#111317));font-weight:var(--hfx-weight-display,600);}
56
+ .cx-pivot-leaf > span{margin-inline-start:var(--hfx-space-6,2rem);}
57
+ .cx-pivot-table thead th:first-child[rowspan]{text-align:left;left:0;--cx-pivot-header-layer:2;z-index:var(--cx-pivot-header-layer);}
58
+ .cx-pivot[data-phone="true"] .cx-pivot-zones{display:none;}
59
+ .cx-pivot-description{display:none;text-align:left;padding:var(--hfx-space-3,0.75rem);font-weight:var(--hfx-weight-semibold,600);}
60
+ .cx-pivot-editor legend{font-weight:var(--hfx-weight-semibold,600);margin-block-end:var(--hfx-space-2,0.5rem);}
61
+ .cx-pivot-editor .cx-pivot-axis{display:flex;flex-wrap:wrap;}
62
+ .cx-pivot-editor .cx-pivot-axis > select{flex:1 1 0;}
63
+ .cx-pivot-editor .cx-pivot-axis > input{flex:1 1 40%;}
64
+ @media (max-width:640px){
65
+ .cx-pivot-editor .cx-pivot-axis > select:last-child{flex-basis:100%;}
66
+ }
67
+ @media print{
68
+ .cx-pivot-description{display:table-caption;}
69
+ .cx-pivot-table{width:100%;min-width:0;}
70
+ .cx-pivot-table th[scope=row]{position:static;}
71
+ }
72
+ .cx-pivot-column-jump{margin-inline-start:var(--hfx-space-1,0.25rem);}
73
+ .cx-pivot-column-jump::before{content:"▾" / "";}
74
+ .cx-pivot-table thead th:first-child[rowspan]{white-space:nowrap;}
75
+ @media print{.cx-pivot-column-jump{display:none !important;}}
@@ -0,0 +1,72 @@
1
+ (function(global){"use strict";const installed=new WeakSet();const part={version:"230b05b7fcab6cf1",requires:["grouping","values","buckets"],install(context){if(installed.has(context.GRID_PIVOT))return;const{DataGrid,GRID_PIVOT,GRID_ROWS,GRID_SHOW_AS,GRID_SHOW_AS_MODES,GRID_BUCKETS,GRID_CALCULATIONS,h,setText,fill,gridButton,gridFreeze,gridQuery,gridQueryStart,gridQueryRows,gridScalar,cellValue,cellText,gridKeyCompare,gridOrder,numberFormat,formatting,gridExportSignal,validateGridPage,checkDocument,validStructuralCount,gridOperationActive,pivotDefinition,pivotIntent,pivotRequestOf,showAsMode,showAsCaption,reportingAggregateName,reportingValueText,UI_IMPL,UI_TOOLS}=context;const pivotColumn=(grid,id)=>grid.view.columns.find(column=>column.id===id);function pivotRaw(grid,level,row){const column=pivotColumn(grid,level.column),value=gridScalar(row.cells[column.id],column);return level.bucket?GRID_BUCKETS.key(level.bucket,value):column.kind==="datetime"&&value!==null?new Date(value).toISOString().replace(".000Z","Z"):value}
2
+ function pivotKey(axis,path,levels,grid){return axis+path.map((value,index)=>{const kind=pivotColumn(grid,levels[index].column).kind;const text=value===null?"z":!levels[index].bucket&&kind==="datetime"?"d"+Date.parse(value):
3
+ typeof value==="string"?"s"+value:typeof value==="boolean"?value?"b1":"b0":"n"+String(value);return "|"+text.length+":"+text}).join("")}
4
+ function pivotCompare(grid,levels,left,right){for(let index=0;index<Math.min(left.length,right.length);index++){const level=levels[index],column=level.bucket?{kind:"number"}:pivotColumn(grid,level.column);const scalar=value=>!level.bucket&&column.kind==="datetime"&&value!==null?Date.parse(value):value;const compare=gridKeyCompare(scalar(left[index]),scalar(right[index]),column,level.direction);if(compare)return compare}
5
+ return left.length-right.length}
6
+ const pivotNumberOrder=(a,b,descending)=>a===null||b===null?a===b?0:a===null?1:-1:descending?b-a:a-b;function pivotInline(grid,definition,query){const source=gridQueryRows(grid.view,query,new Map(grid.view.columns.map(column=>[column.id,column])),null);if(source.length>100000)throw new Error("CanopyX: inline pivot supports at most 100,000 records; use a hosted pivot for larger datasets.");const aggregates=definition.values.map(id=>grid.view.reporting.aggregates.find(value=>value.id===id));const aggregate=rows=>Object.fromEntries(aggregates.map(value=>[value.id,GRID_ROWS.aggregateValue(grid,value,rows,0,rows.length)]));const paths=new Map(),contributions=source.map(row=>{const rows=definition.rows.map(level=>pivotRaw(grid,level,row)),columns=definition.columns.map(level=>pivotRaw(grid,level,row));const key=pivotKey("c",columns,definition.columns,grid);if(!paths.has(key))paths.set(key,{key,path:columns,members:[]});paths.get(key).members.push(row);return{rows,columns,row}});let leaves=[...paths.values()].sort((a,b)=>pivotCompare(grid,definition.columns,a.path,b.path));const requested=Math.min(definition.maxColumns,definition.topNColumns??definition.maxColumns),limited=leaves.length>requested;if(limited){const totals=new Map(leaves.map(leaf=>[leaf.key,aggregate(leaf.members)[definition.values[0]]]));leaves.sort((a,b)=>pivotNumberOrder(totals.get(a.key),totals.get(b.key),true)||pivotCompare(grid,definition.columns,a.path,b.path));leaves=leaves.slice(0,Math.min(requested,definition.maxColumns-1))}
7
+ const kept=new Set(leaves.map(leaf=>leaf.key)),rowNodes=new Map(),columnNodes=new Map(),members=new Map();const nodes=(map,axis,path,levels)=>{for(let depth=0;depth<=path.length;depth++){const prefix=path.slice(0,depth),key=pivotKey(axis,prefix,levels,grid);if(!map.has(key)){if(map.size>=10001)throw new Error("CanopyX: a pivot axis supports at most 10,000 prefixes.");map.set(key,{key,...(depth?{parent:pivotKey(axis,path.slice(0,depth-1),levels,grid)}:{}),path:prefix,other:false})}}};nodes(rowNodes,"r",[],definition.rows);nodes(columnNodes,"c",[],definition.columns);leaves.forEach(leaf=>nodes(columnNodes,"c",leaf.path,definition.columns));if(limited)columnNodes.set("c:other",{key:"c:other",parent:"c",path:[],other:true});let count=0;const at=(r,c)=>{if(!members.has(r))members.set(r,new Map());if(!members.get(r).has(c)){if(++count>100000)throw new Error("CanopyX: pivot exceeds 100,000 populated prefix intersections.");members.get(r).set(c,[])}
8
+ return members.get(r).get(c)};at("r","c");for(const entry of contributions){nodes(rowNodes,"r",entry.rows,definition.rows);if(rowNodes.size*columnNodes.size*aggregates.length>100000)throw new Error("CanopyX: pivot tables support at most 100,000 display cells. Narrow the query, axes or values.");const other=limited&&!kept.has(pivotKey("c",entry.columns,definition.columns,grid));for(let r=0;r<=entry.rows.length;r++){const row=pivotKey("r",entry.rows.slice(0,r),definition.rows,grid);at(row,"c").push(entry.row);if(other)at(row,"c:other").push(entry.row);for(let c=1;c<=entry.columns.length;c++){const column=pivotKey("c",entry.columns.slice(0,c),definition.columns,grid);if(columnNodes.has(column))at(row,column).push(entry.row)}}}
9
+ const cells=[],lookup=new Map();for(const[row,columns]of members)for(const[column,records]of columns){const cell={row,column,count:records.length,aggregates:aggregate(records)};cells.push(cell);if(!lookup.has(row))lookup.set(row,new Map());lookup.get(row).set(column,cell)}
10
+ const order=(nodes,levels,rows)=>{const result=[],children=new Map();for(const node of nodes.values()){const parent=node.parent||"";if(!children.has(parent))children.set(parent,[]);children.get(parent).push(node)}
11
+ const walk=parent=>{const siblings=children.get(parent)||[];siblings.sort((a,b)=>{if(a.other||b.other)return a.other===b.other?0:a.other?1:-1;if(rows&&definition.sortByTotal){const id=definition.sortByTotal.aggregate,sorted=pivotNumberOrder(lookup.get(a.key).get("c").aggregates[id],lookup.get(b.key).get("c").aggregates[id],definition.sortByTotal.direction==="desc");if(sorted)return sorted}
12
+ return pivotCompare(grid,levels,a.path,b.path)});for(const node of siblings){result.push(node);walk(node.key)}};walk("");return result};return{protocol:"canopyx/2",type:"gridPivot",view:grid.view.id,matched:source.length,distinctColumns:paths.size,columnsLimited:limited,definition,rows:order(rowNodes,definition.rows,true),columns:order(columnNodes,definition.columns,false),retainedColumns:leaves.map(leaf=>leaf.path),cells,query,revision:query.revision}}
13
+ Object.assign(GRID_PIVOT,{inline:pivotInline,key:pivotKey});function pivotResult(grid,definition,query,page,input){checkDocument(input,"gridPivot");if(input.view!==grid.view.id||input.revision!==page.revision||input.matched!==page.matched||
14
+ !validStructuralCount(input.matched)||!validStructuralCount(input.distinctColumns)||typeof input.columnsLimited!=="boolean"||
15
+ JSON.stringify(pivotDefinition(grid.view,input.definition))!==JSON.stringify(definition)||JSON.stringify(gridQuery(grid.view,input.query,true))!==JSON.stringify(query)||
16
+ !Array.isArray(input.rows)||!Array.isArray(input.columns)||!Array.isArray(input.cells)||input.cells.length>100000||
17
+ !Array.isArray(input.retainedColumns)||input.retainedColumns.length>definition.maxColumns)throw new Error("CanopyX: pivot ownership or bounds differ from the accepted query.");const axis=(nodes,levels,letter,limit)=>{if(!nodes.length||nodes.length>limit)throw new Error("CanopyX: pivot axis exceeds its bounds.");const found=new Map();for(const node of nodes){if(!node||typeof node!=="object"||!Array.isArray(node.path)||node.path.length>levels.length||typeof node.key!=="string"||typeof node.other!=="boolean"||
18
+ found.has(node.key)||node.other&&(letter!=="c"||node.key!=="c:other"||node.parent!=="c"||node.path.length)||
19
+ !node.other&&(pivotKey(letter,node.path,levels,grid)!==node.key||node.path.length===0&&node.parent!==undefined||
20
+ node.path.length>0&&(!found.has(node.parent)||node.parent!==pivotKey(letter,node.path.slice(0,-1),levels,grid))))throw new Error("CanopyX: invalid pivot axis prefix.");node.path.forEach((value,index)=>{if(value!==null)gridScalar(value,levels[index].bucket?{kind:"number"}:pivotColumn(grid,levels[index].column))});found.set(node.key,node)}
21
+ if(nodes[0].key!==letter||nodes.filter(node=>!nodes.some(child=>child.parent===node.key)).length>(letter==="c"?definition.maxColumns:10000))throw new Error("CanopyX: invalid pivot leaf bound.");return found};const rows=axis(input.rows,definition.rows,"r",10001),columns=axis(input.columns,definition.columns,"c",402),lookup=new Map();if(rows.size*columns.size*definition.values.length>100000)throw new Error("CanopyX: pivot tables support at most 100,000 display cells. Narrow the query, axes or values.");for(const cell of input.cells){if(!cell||!rows.has(cell.row)||!columns.has(cell.column)||!validStructuralCount(cell.count)||cell.count>input.matched||
22
+ !cell.aggregates||typeof cell.aggregates!=="object"||Array.isArray(cell.aggregates)||Object.keys(cell.aggregates).length!==definition.values.length)throw new Error("CanopyX: invalid pivot intersection.");for(const id of definition.values){const value=cell.aggregates[id],aggregate=grid.view.reporting.aggregates.find(item=>item.id===id),counting=["count","distinctCount"].includes(aggregate.operation);if(!Object.hasOwn(cell.aggregates,id)||value!==null&&(typeof value!=="number"||!Number.isFinite(value)||Math.abs(value)>Number.MAX_SAFE_INTEGER)||
23
+ counting&&(!validStructuralCount(value)||value>cell.count)||!cell.count&&!counting&&value!==null)throw new Error("CanopyX: invalid pivot aggregate.")}
24
+ if(!lookup.has(cell.row))lookup.set(cell.row,new Map());if(lookup.get(cell.row).has(cell.column))throw new Error("CanopyX: duplicate pivot intersection.");lookup.get(cell.row).set(cell.column,cell)}
25
+ if(lookup.get("r")?.get("c")?.count!==input.matched)throw new Error("CanopyX: pivot grand total differs.");for(const[nodes,rowAxis]of[[rows,true],[columns,false]]){const children=new Map();for(const node of nodes.values())if(node.parent){if(!children.has(node.parent))children.set(node.parent,[]);children.get(node.parent).push(node.key)}
26
+ for(const[parent,keys]of children)for(const opposite of(rowAxis?columns:rows).keys()){const at=key=>(rowAxis?lookup.get(key)?.get(opposite):lookup.get(opposite)?.get(key))?.count||0;const covered=keys.reduce((sum,key)=>sum+at(key),0);if((rowAxis||!input.columnsLimited)&&covered!==at(parent))throw new Error("CanopyX: pivot prefix counts do not cover their parent.")}}
27
+ const retained=new Set();for(const path of input.retainedColumns){if(!Array.isArray(path)||path.length!==definition.columns.length)throw new Error("CanopyX: invalid retained pivot column path.");const key=pivotKey("c",path,definition.columns,grid);if(!columns.has(key)||retained.has(key))throw new Error("CanopyX: retained pivot columns differ.");retained.add(key)}
28
+ if(input.columnsLimited!==columns.has("c:other")||input.columnsLimited&&input.distinctColumns<=retained.size)throw new Error("CanopyX: invalid pivot column-cap metadata.");return gridFreeze(JSON.parse(JSON.stringify(input)))}
29
+ const PIVOT_LOOKUPS=new WeakMap();function pivotCell(result,row,column){let lookup=PIVOT_LOOKUPS.get(result);if(!lookup){lookup=new Map();for(const cell of result.cells){if(!lookup.has(cell.row))lookup.set(cell.row,new Map());lookup.get(cell.row).set(cell.column,cell)}
30
+ PIVOT_LOOKUPS.set(result,lookup)}
31
+ return lookup.get(row)?.get(column)||{row,column,count:0,aggregates:null}}
32
+ function pivotDisplay(grid,result,row,column,aggregate){const node=result.rows.find(node=>node.key===row),siblings=result.rows.filter(sibling=>sibling.parent===node.parent),mode=showAsMode(grid,aggregate);const raw=(r,c)=>pivotCell(result,r,c).aggregates?.[aggregate.id]??(["count","distinctCount"].includes(aggregate.operation)?0:null);const values=siblings.map(sibling=>raw(sibling.key,column));const displayed=GRID_SHOW_AS.evaluate(values,mode,raw("r","c"),raw(node.parent||"r",column),siblings.map(sibling=>raw(sibling.key,"c")),siblings.map(()=>raw("r",column)));return{value:displayed[siblings.findIndex(sibling=>sibling.key===row)],mode,numberFormat:GRID_SHOW_AS.numberFormat(mode)}}
33
+ Object.assign(GRID_PIVOT,{validate:pivotResult,cell:pivotCell,display:pivotDisplay});const PIVOT_RENDER_BASE=DataGrid.prototype.render,PIVOT_DESTROY_BASE=DataGrid.prototype.destroy;Object.assign(DataGrid.prototype,{async requestPivot(query,page,signal,restore,state){const{query:accepted,definition}=pivotRequestOf(this,query,page.revision,state),key=JSON.stringify({query:accepted,definition});if(this.pivotCandidate?.key===key){if(restore)restore.pivotCandidate=this.pivotCandidate;return}
34
+ const request={protocol:"canopyx/2",type:"gridPivotQuery",query:accepted,definition};const value=this.source.pivot?await this.runGridHook(signal=>this.source.pivot(request,{signal}),signal):this.view.source.kind==="inline"?pivotInline(this,definition,accepted):null;gridOperationActive(this,signal);if(!value)throw new Error("CanopyX: hosted pivots need an explicit source.pivot hook.");const candidate={key,query:accepted,result:pivotResult(this,definition,accepted,page,value)};if(restore)restore.pivotCandidate=candidate;else this.pivotCandidate=candidate},render(){if(!pivotEnabledLocal(this)||!pivotIntent(this)?.active){if(this.pivotButton)setText(this.pivotButton,this.labels.gridPivot);this.host.classList.remove("cx-pivot-active");if(this.pivotPanel)this.pivotPanel.hidden=true;if(this.pivotOwnHeight){this.host.style.maxHeight=this.pivotFlatMaxHeight;this.pivotOwnHeight=false}
35
+ return PIVOT_RENDER_BASE.call(this)}
36
+ if(this.destroyed||!this.acceptedQuery||!this.pivotCandidate)return;const key=JSON.stringify(pivotRequestOf(this,this.acceptedQuery,this.snapshot.revision));if(key!==this.pivotCandidate.key){if(this.pivotPanel)this.pivotPanel.hidden=true;return}
37
+ this.pivotResult=this.pivotCandidate.result;if(!this.pivotOwnHeight){this.pivotFlatMaxHeight=this.host.style.maxHeight;this.pivotOwnHeight=true;if(this.fitInline!==undefined)this.host.style.maxHeight=this.fitInline}
38
+ this.host.classList.add("cx-pivot-active");if(!this.pivotPanel){this.pivotPanel=h("section","cx-pivot");this.pivotPanel.setAttribute("aria-label",this.labels.gridPivot);this.grid.after(this.pivotPanel);this.pivotPanel.tabIndex=0;this.pivotRevealData=true;this.pivotResize=new ResizeObserver(()=>this.renderPivot());this.pivotResize.observe(this.pivotPanel)}
39
+ if(this.pivotPanel.hidden)this.pivotRevealData=true;this.pivotPanel.hidden=false;this.renderPivot()},setPivotCollapsed(axis,key,collapsed){const state=pivotIntent(this),nodes=axis==="rows"?this.pivotResult?.rows:axis==="columns"?this.pivotResult?.columns:null;if(!state?.active||!nodes?.some(node=>node.key===key&&node.parent&&nodes.some(child=>child.parent===key))||typeof collapsed!=="boolean"||this.pendingQuery)
40
+ throw new Error("CanopyX: choose an accepted pivot axis prefix and collapse intent.");const name=axis==="rows"?"collapsedRows":"collapsedColumns",keys=new Set(state[name]);if(collapsed)keys.add(key);else keys.delete(key);state[name]=[...keys];this.renderPivot();this.noteViewPreferenceIntent?.();this.stateChanged();this.emit("pivotCollapse",{axis,key,collapsed})},async drillPivot(rowKey,columnKey){const state=pivotIntent(this),result=this.pivotResult,row=result.rows.find(node=>node.key===rowKey),column=result.columns.find(node=>node.key===columnKey);if(!state?.active||!row||!column||this.pendingQuery)throw new Error("CanopyX: choose an accepted pivot intersection.");const filters=[];const add=(levels,paths,width,exclude=false)=>{if(width)filters.push({columns:levels.slice(0,width).map(level=>({column:level.column,...(level.bucket?{bucket:level.bucket}:{})})),values:paths,exclude})};add(state.rows,[row.path],row.path.length);if(column.other&&result.retainedColumns.length)add(state.columns,result.retainedColumns,state.columns.length,true);else if(!column.other)add(state.columns,[column.path],column.path.length);const previous=this.getQuery(),next=gridQueryStart(previous,{tupleFilters:[...(previous.tupleFilters||[]),...filters]});gridQuery(this.view,next);this.pivotDrill={query:previous};state.active=false;try{await this.setQuery(next)}
41
+ catch(error){state.active=true;this.pivotDrill=null;this.render();throw error}
42
+ this.showPivotReturn();this.stateChanged()},showPivotReturn(){if(!this.pivotDrill)return;if(!this.pivotReturn){this.pivotReturn=h("div","cx-pivot-return");this.notice.before(this.pivotReturn)}
43
+ this.pivotReturn.replaceChildren(gridButton(this.labels.gridPivotReturn,async()=>{const previous=this.pivotDrill;if(!previous||this.pendingQuery)return;pivotIntent(this).active=true;try{await this.setQuery(gridQueryStart(previous.query,{}));this.pivotDrill=null;this.pivotReturn.remove();this.pivotReturn=null;this.stateChanged()}
44
+ catch(error){pivotIntent(this).active=false;this.render();this.emit("error",{error})}}))},destroy(){this.pivotResize?.disconnect();this.pivotPanel?.remove();this.pivotReturn?.remove();return PIVOT_DESTROY_BASE.call(this)}});const pivotEnabledLocal=grid=>!!grid.view.reporting?.pivot||!!grid.view.reporting?.pivotable;function pivotLabel(grid,definition,axis,node,full=true){if(node.other)return grid.labels.gridPivotOther;if(!node.parent)return grid.labels.gridSummary;const levels=definition[axis];return node.path.map((value,index)=>{const level=levels[index],column=pivotColumn(grid,level.column);return value===null?grid.labels.gridGroupMissing:level.bucket?GRID_BUCKETS.label(level.bucket,value,grid.lang):cellText(value,column,grid.fmt)}).slice(full?0:-1).join(" / ")}
45
+ function pivotVisible(nodes,collapsed,leavesOnly){const byKey=new Map(nodes.map(node=>[node.key,node])),closed=new Set(collapsed);return nodes.filter(node=>{if(!node.parent)return false;let parent=byKey.get(node.parent);while(parent){if(closed.has(parent.key))return false;parent=byKey.get(parent.parent)}
46
+ return!leavesOnly||closed.has(node.key)||!nodes.some(child=>child.parent===node.key)})}
47
+ function pivotToggle(grid,node,axis){const state=pivotIntent(grid),collapsed=state[axis==="rows"?"collapsedRows":"collapsedColumns"].includes(node.key);const button=gridButton(collapsed?"▸":"▾",()=>grid.setPivotCollapsed(axis,node.key,!collapsed));button.classList.add("cx-pivot-collapse");button.setAttribute("aria-label",(collapsed?grid.labels.gridGroupExpand:grid.labels.gridGroupCollapse)+ ": "+pivotLabel(grid,state,axis,node));button.setAttribute("aria-expanded",String(!collapsed));button.dataset.pivotFocus=axis+":"+node.key;return button}
48
+ DataGrid.prototype.renderPivot=function(){if(!this.pivotPanel||this.pivotPanel.hidden||!pivotIntent(this)?.active||!this.pivotResult||this.destroyed)return;this.updateGridPhone();const state=pivotIntent(this),result=this.pivotResult,phone=this.host.clientWidth<=640,active=document.activeElement;const hadFocus=this.pivotPanel.contains(active),focus=hadFocus?active.dataset.pivotFocus:null;const columns=pivotVisible(result.columns,state.collapsedColumns,true),rows=pivotVisible(result.rows,state.collapsedRows,false),totalColumn=result.columns[0];const aggregates=state.values.map(id=>this.view.reporting.aggregates.find(value=>value.id===id));const zones=this.pivotZones(),displayedColumns=[...columns,totalColumn];this.pivotPanel.dataset.phone=String(phone);if(this.pivotButton)setText(this.pivotButton,phone?this.labels.gridPivotConfigure:this.labels.gridPivot);const scroll=h("div","cx-pivot-scroll"),table=h("table","cx-pivot-table"),caption=h("caption","cx-pivot-description");const describe=axis=>state[axis].map(level=>pivotColumn(this,level.column).title).join(" / ");setText(caption,this.view.label+" · "+describe("rows")+(state.columns.length?" × "+describe("columns"):"")+ " · "+aggregates.map(value=>reportingAggregateName(this,value)).join(", "));table.append(caption);scroll.tabIndex=0;scroll.setAttribute("aria-label",this.labels.gridPivot);const head=h("thead"),axisDepth=Math.max(1,state.columns.length),leading=h("th");leading.scope="col";leading.rowSpan=axisDepth+1;setText(leading,describe("rows")||this.view.label);const jump=gridButton("",()=>this.openPivotColumnMenu(jump).catch(error=>this.emit("error",{error})));jump.classList.add("cx-pivot-column-jump");jump.dataset.size="sm";jump.dataset.pivotFocus="column-picker";jump.setAttribute("aria-label",this.labels.gridPivotColumn);jump.setAttribute("aria-haspopup","menu");jump.title=this.labels.gridPivotColumn;leading.append(jump);const ancestor=(node,depth)=>{while(node.path.length>depth)node=result.columns.find(parent=>parent.key===node.parent);return node};for(let depth=0;depth<axisDepth;depth++){const row=h("tr");if(!depth)row.append(leading);for(let index=0;index<displayedColumns.length;){const column=displayedColumns[index];if(depth&&column.path.length<=depth){index++;continue}
49
+ const group=ancestor(column,depth+1);let end=index+1;while(end<displayedColumns.length&&ancestor(displayedColumns[end],depth+1).key===group.key)end++;const th=h("th"),label=h("span");th.scope="colgroup";th.colSpan=(end-index)*aggregates.length;th.rowSpan=group.path.length<axisDepth?axisDepth-depth:1;setText(label,pivotLabel(this,state,"columns",group,false));label.title=pivotLabel(this,state,"columns",group);if(group.parent&&result.columns.some(child=>child.parent===group.key))th.append(pivotToggle(this,group,"columns"));th.append(label);row.append(th);index=end}
50
+ head.append(row)}
51
+ const measures=h("tr");for(const column of displayedColumns)for(const aggregate of aggregates){const th=h("th"),value=h("button","cx-pivot-value-menu");th.scope="col";th.dataset.pivotColumn=column.key;setText(value,reportingAggregateName(this,aggregate));value.type="button";value.title=this.labels.gridShowAs;value.dataset.pivotFocus="value:"+column.key+":"+aggregate.id;value.addEventListener("click",()=>this.pivotValueMenu(aggregate,value));th.append(value);const mode=showAsMode(this,aggregate);if(mode!=="value"){const label=h("span","cx-pivot-caption");setText(label,showAsCaption(this,mode));th.append(label)}
52
+ measures.append(th)}
53
+ head.append(measures);table.append(head);const body=h("tbody");const appendRow=(node,parent)=>{const row=h("tr"),title=h("th"),text=h("span"),hasChildren=result.rows.some(child=>child.parent===node.key);title.scope="row";title.style.setProperty("--cx-pivot-depth",String(Math.max(0,node.path.length-1)));setText(text,pivotLabel(this,state,"rows",node,false));text.title=pivotLabel(this,state,"rows",node);if(hasChildren&&node.parent)title.append(pivotToggle(this,node,"rows"));title.append(text);title.classList.toggle("cx-pivot-leaf",!hasChildren);row.append(title);if(hasChildren||!node.parent)row.classList.add("cx-pivot-subtotal");for(const column of displayedColumns)for(const aggregate of aggregates){const cell=h("td"),button=h("button","cx-pivot-cell"),display=pivotDisplay(this,result,node.key,column.key,aggregate);setText(button,reportingValueText(this,aggregate,display.value,display.mode));button.type="button";button.title=fill(this.labels.gridPivotDrill,{row:pivotLabel(this,state,"rows",node),column:pivotLabel(this,state,"columns",column)});button.setAttribute("aria-label",button.title+": "+reportingAggregateName(this,aggregate)+ ", "+button.textContent);button.dataset.pivotFocus="cell:"+node.key+":"+column.key+":"+aggregate.id;button.addEventListener("click",()=>this.drillPivot(node.key,column.key).catch(error=>this.emit("error",{error})));cell.append(button);row.append(cell)}
54
+ parent.append(row)};rows.forEach(row=>appendRow(row,body));const foot=h("tfoot");appendRow(result.rows[0],foot);table.append(body,foot);scroll.append(table);const note=h("p","cx-pivot-note");note.hidden=!result.columnsLimited;if(result.columnsLimited)setText(note,fill(this.labels.gridPivotLimited,{shown:String(result.retainedColumns.length),total:String(result.distinctColumns)}));this.pivotPanel.replaceChildren(zones,note,scroll);let offset=0;for(const row of head.rows){for(const cell of row.cells)cell.style.top=offset+"px";offset +=row.getBoundingClientRect().height}
55
+ if(focus)[...this.pivotPanel.querySelectorAll("[data-pivot-focus]")].find(control=>control.dataset.pivotFocus===focus)?.focus({preventScroll:true})};DataGrid.prototype.openPivotColumnMenu=async function(anchor){await this.requirePart("filter-editor");await this.requirePart("ui-menu");if(this.destroyed||this.pendingQuery||!pivotIntent(this)?.active)return;const state=pivotIntent(this),result=this.pivotResult,columns=[...pivotVisible(result.columns,state.collapsedColumns,true),result.columns[0]];return this.openGridPopover(anchor,"cx-pivot-column-menu",this.labels.gridPivotColumn,null,(panel,signal)=>{UI_TOOLS.menuItems(panel,columns.map(column=>({id:column.key,label:pivotLabel(this,state,"columns",column)})));return UI_IMPL.menu(panel,{host:this.host,trigger:anchor,anchored:false,bindTrigger:false,decorateTrigger:false,label:this.labels.gridPivotColumn,onSelect:item=>{const scroll=this.pivotPanel.querySelector(".cx-pivot-scroll"),table=scroll.querySelector("table");const cell=[...table.querySelectorAll("[data-pivot-column]")].find(cell=>cell.dataset.pivotColumn===item.id);if(!cell)return;scroll.scrollLeft +=cell.getBoundingClientRect().left-scroll.getBoundingClientRect().left-table.tHead.rows[0].cells[0].getBoundingClientRect().width}},signal)})};DataGrid.prototype.pivotZones=function(){const state=pivotIntent(this),zones=h("div","cx-pivot-zones");zones.setAttribute("role","group");zones.setAttribute("aria-label",this.labels.gridPivotConfigure);for(const[axis,label]of[["rows",this.labels.gridPivotRows],["columns",this.labels.gridPivotColumns],["values",this.labels.gridPivotValues]]){const zone=h("div","cx-pivot-zone"),title=h("span");setText(title,label);zone.append(title);const entries=axis==="values"?state.values.map(id=>reportingAggregateName(this,this.view.reporting.aggregates.find(value=>value.id===id))):state[axis].map(level=>
56
+ pivotColumn(this,level.column).title +(level.bucket?" · "+this.labels["gridBucket"+level.bucket.kind[0].toUpperCase()+ level.bucket.kind.slice(1)]:""));for(const entry of entries){const chip=h("span","cx-gchip"),name=h("span","cx-gchip-name");setText(name,entry);chip.title=entry;chip.append(name);zone.append(chip)}
57
+ const edit=gridButton("+",()=>this.configurePivot(edit));edit.dataset.size="sm";edit.title=this.labels.gridPivotFieldOptions+": "+label;edit.setAttribute("aria-label",edit.title);edit.dataset.pivotFocus="zone:"+axis;zone.append(edit);zones.append(zone)}
58
+ zones.append(gridButton(this.labels.gridPivotFlat,()=>this.setPivot(false).catch(error=>this.emit("error",{error}))));return zones};DataGrid.prototype.pivotValueMenu=async function(aggregate,button){await this.requirePart("filter-editor");if(this.destroyed||this.pendingQuery)return;const controls=h("form","cx-show-as-fields"),label=h("label","cx-show-as-field"),text=h("span"),select=h("select","cx-input");setText(text,this.labels.gridShowAs);select.setAttribute("aria-label",this.labels.gridShowAs);for(const mode of GRID_SHOW_AS_MODES){const option=h("option");option.value=mode;setText(option,this.labels["gridShowAs"+mode[0].toUpperCase()+ mode.slice(1)]);select.append(option)}
59
+ select.value=showAsMode(this,aggregate);const apply=gridButton(this.labels.gridApply,()=>{});apply.type="submit";apply.dataset.variant="primary";controls.addEventListener("submit",event=>{event.preventDefault();this.setAggregateShowAs(aggregate.id,select.value).then(()=>this.reportingText?._cxOverlay.close()).catch(error=>this.emit("error",{error}))});label.append(text,select);controls.append(label,apply);this.showReportingText({...pivotColumn(this,aggregate.column),title:reportingAggregateName(this,aggregate)},"",button,undefined,false,controls)};DataGrid.prototype.configurePivot=async function(button){await this.requirePart("filter-editor");if(this.destroyed||this.pendingQuery)return;const state=pivotIntent(this),initial=state||{rows:[],columns:[],values:[this.view.reporting.aggregates[0].id],maxColumns:200};const form=h("form","cx-pivot-editor"),axes={},values=[];const option=(select,value,text)=>{const entry=h("option");entry.value=value;setText(entry,text);select.append(entry)};for(const[axis,maximum,title]of[["rows",4,this.labels.gridPivotRows],["columns",2,this.labels.gridPivotColumns]]){const group=h("fieldset"),legend=h("legend");setText(legend,title);group.append(legend);axes[axis]=[];for(let index=0;index<maximum;index++){const line=h("label","cx-pivot-axis"),column=h("select","cx-input"),bucket=h("select","cx-input"),width=h("input","cx-input"),origin=h("input","cx-input"),direction=h("select","cx-input");column.setAttribute("aria-label",title+" " +(index+1));bucket.setAttribute("aria-label",title+" " +(index+1)+ ": "+this.labels.gridBucket);option(column,"","—");for(const item of this.view.columns.filter(column=>column.sortable&&column.filterable!==false))option(column,item.id,item.title);const current=initial[axis][index];column.value=current?.column||"";direction.setAttribute("aria-label",title+" " +(index+1)+ ": "+this.labels.gridGroupSortBy);option(direction,"asc",this.labels.gridSortAscending);option(direction,"desc",this.labels.gridSortDescending);direction.value=current?.direction||"asc";width.type="text";width.maxLength=4096;origin.type="number";origin.step="any";width.value=current?.bucket?.edges?.join(", ")||String(current?.bucket?.width??10);origin.value=String(current?.bucket?.origin??0);const update=()=>{const old=bucket.value,selected=pivotColumn(this,column.value);bucket.replaceChildren();option(bucket,"",this.labels.gridBucketNone);const kinds=selected?.kind==="datetime"?["day","week","month","quarter","year","hour","weekday"]:selected?.kind==="number"?["width","edges"]:[];for(const kind of kinds)option(bucket,kind,this.labels["gridBucket"+kind[0].toUpperCase()+ kind.slice(1)]);bucket.value=kinds.includes(old)?old:"";bucket.hidden=!kinds.length;direction.hidden=!selected;range()};const range=()=>{width.hidden=!["width","edges"].includes(bucket.value);origin.hidden=bucket.value!=="width";width.setAttribute("aria-label",bucket.value==="edges"?this.labels.gridBucketEdges:this.labels.gridBucketWidth);origin.setAttribute("aria-label",this.labels.gridBucketOrigin)};column.addEventListener("change",update);bucket.addEventListener("change",range);update();bucket.value=current?.bucket?.kind||"";range();line.append(column,bucket,width,origin,direction);group.append(line);axes[axis].push(()=>{if(!column.value)return null;const kind=bucket.value,spec=!kind?undefined:kind==="width"?{kind,width:width.value.trim()?Number(width.value):NaN,origin:origin.value.trim()?Number(origin.value):NaN}:kind==="edges"?
60
+ {kind,edges:width.value.split(",").map(value=>value.trim()?Number(value):NaN)}:{kind,timeZone:current?.bucket?.timeZone||"UTC",firstWeekday:current?.bucket?.firstWeekday??1};return{column:column.value,direction:direction.value,...(spec?{bucket:spec}:{})}})}
61
+ form.append(group)}
62
+ const group=h("fieldset"),legend=h("legend");setText(legend,this.labels.gridPivotValues);group.append(legend);for(const aggregate of this.view.reporting.aggregates){const label=h("label"),check=h("input"),text=h("span");check.type="checkbox";check.className="cx-check";check.checked=initial.values.includes(aggregate.id);setText(text,reportingAggregateName(this,aggregate));label.append(check,text);group.append(label);values.push(()=>check.checked?aggregate.id:null)}
63
+ const topLabel=h("label","cx-pivot-setting"),topText=h("span"),top=h("input","cx-input");top.type="number";top.placeholder="1–200";top.min="1";top.max="200";top.value=initial.topNColumns===undefined?"":String(initial.topNColumns);setText(topText,this.labels.gridPivotTopN);topLabel.append(topText,top);top.setAttribute("aria-label",this.labels.gridPivotTopN);const sortLabel=h("label","cx-pivot-setting"),sortText=h("span"),sort=h("select","cx-input"),descending=h("select","cx-input");setText(sortText,this.labels.gridPivotSort);option(sort,"",this.labels.gridGroupSortValue);for(const aggregate of this.view.reporting.aggregates)option(sort,aggregate.id,reportingAggregateName(this,aggregate));sort.value=initial.sortByTotal?.aggregate||"";sort.setAttribute("aria-label",this.labels.gridPivotSort);option(descending,"desc",this.labels.gridSortDescending);option(descending,"asc",this.labels.gridSortAscending);descending.value=initial.sortByTotal?.direction||"desc";sortLabel.append(sortText,sort,descending);const error=h("p","cx-pivot-note");error.setAttribute("role","alert");error.hidden=true;const apply=gridButton(this.labels.gridApply,()=>{});apply.type="submit";apply.dataset.variant="primary";const flat=gridButton(this.labels.gridPivotFlat,()=>{this.reportingText?._cxOverlay.close();this.setPivot(false).catch(error=>this.emit("error",{error}))});flat.hidden=!state?.active;form.append(group,topLabel,sortLabel,flat,error,apply);for(const select of form.querySelectorAll("select")){const title=()=>{select.title=select.selectedOptions[0]?.textContent||""};title();select.addEventListener("change",title)}
64
+ form.addEventListener("submit",event=>{event.preventDefault();try{const definition=pivotDefinition(this.view,{rows:axes.rows.map(read=>read()).filter(Boolean),columns:axes.columns.map(read=>read()).filter(Boolean),values:values.map(read=>read()).filter(Boolean),maxColumns:initial.maxColumns,...(top.value.trim()?{topNColumns:Number(top.value)}:{}),...(sort.value?{sortByTotal:{aggregate:sort.value,direction:descending.value}}:{})});this.reportingText?._cxOverlay.close();this.setPivot(definition).catch(error=>this.emit("error",{error}))}catch(_){error.hidden=false;setText(error,this.labels.gridPivotInvalid)}});this.showReportingText({...this.view.columns[0],title:this.labels.gridPivotConfigure},"",button,undefined,false,form)};function pivotExportAxis(nodes,collapsed){const children=new Map(),ordered=[],closed=new Set(collapsed);for(const node of nodes)if(node.parent){if(!children.has(node.parent))children.set(node.parent,[]);children.get(node.parent).push(node)}
65
+ const visit=(node,hidden)=>{const descendants=children.get(node.key)||[];ordered.push({node,axisKey:node.key,outlineLevel:Math.max(0,node.path.length-1),summary:!node.parent||!!descendants.length,grandTotal:!node.parent,collapsed:closed.has(node.key),hidden});for(const child of descendants)visit(child,hidden||closed.has(node.key))};visit(nodes[0],false);return ordered.slice(1).concat(ordered.slice(0,1))}
66
+ DataGrid.prototype.pivotExport=function(){const state=pivotIntent(this),result=this.pivotResult;if(!state?.active||!result||this.pendingQuery)throw new Error("CanopyX: export needs an accepted pivot.");const aggregates=state.values.map(id=>this.view.reporting.aggregates.find(value=>value.id===id)),columns=[{id:"axis",title:this.labels.gridPivotRows,kind:"text"}];const selected=pivotExportAxis(result.columns,state.collapsedColumns),rowAxis=pivotExportAxis(result.rows,state.collapsedRows),rows=[],columnGroups=[];selected.forEach(({node,...presentation},ci)=>aggregates.forEach((aggregate,ai)=>columns.push({id:"v"+ci+"_"+ai,title:pivotLabel(this,state,"columns",node)+ ": "+reportingAggregateName(this,aggregate),kind:"number",aggregate:aggregate.id,...presentation})));selected.forEach(({node,collapsed,hidden},index)=>{if(!node.parent||!selected.some(item=>item.node.parent===node.key))return;let end=index+1;while(end<selected.length&&selected[end].node.path.length>node.path.length)end++;columnGroups.push({key:node.key,...(node.parent==="c"?{}:{parent:node.parent}),firstColumn:1 +(index+1)*aggregates.length,columnCount:(end-index-1)*aggregates.length,depth:node.path.length,collapsed,hidden})});for(const{node,...presentation}of rowAxis){const cells={axis:{value:pivotLabel(this,state,"rows",node,false),text:pivotLabel(this,state,"rows",node,false)}};selected.forEach(({node:column},ci)=>aggregates.forEach((aggregate,ai)=>{const display=pivotDisplay(this,result,node.key,column.key,aggregate);cells["v"+ci+"_"+ai]={...display,text:display.value===null?"":reportingValueText(this,aggregate,display.value,display.mode)}}));rows.push({id:"p"+rows.length,...presentation,cells})}
67
+ return gridFreeze({view:this.view.id,query:result.query,definition:result.definition,columns,rows,columnGroups,rowAxis:result.rows,columnAxis:result.columns,matched:result.matched,distinctColumns:result.distinctColumns,retainedColumns:result.retainedColumns,columnsLimited:result.columnsLimited})};const PIVOT_EXPORT_ROWS=new WeakMap(),PIVOT_EXPORT_OWNERS=new WeakMap(),PIVOT_EXPORT_BASE=DataGrid.prototype.exportRows;GRID_PIVOT.prepareExport=(grid,options,presentColumn)=>{if(options.pivot!==true||options.scope!=="filtered"||options.columns!==undefined)
68
+ throw new Error("CanopyX: pivot table capture needs filtered scope and the complete pivot columns.");if(options.values!==undefined&&!["raw","display"].includes(options.values))throw new Error("CanopyX: export values must be raw/display.");if(options.signal!==undefined&&!(options.signal instanceof AbortSignal))throw new Error("CanopyX: export requires AbortSignal.");const presentation=Object.create(grid);presentation.lang=options.locale||grid.lang;presentation.local=(options.timeZone||(grid.local?"local":"utc"))==="local";presentation.fmt=formatting(presentation.lang,presentation.local,grid.labels);const snapshot=grid.pivotExport.call(presentation);const{definition,rowAxis,columnAxis,columnGroups,distinctColumns,retainedColumns,columnsLimited}=snapshot;const columns=snapshot.columns.map(presentColumn);const pivot=gridFreeze({definition,rowAxis,columnAxis,columnGroups,distinctColumns,retainedColumns,columnsLimited});const request=gridFreeze({scope:"filtered",query:snapshot.query,revision:snapshot.query.revision,total:grid.snapshot.total,matched:snapshot.matched,recordCount:snapshot.rows.length,selection:[],columns,presentation:"semantic",values:options.values||"display",timeZone:presentation.local?"local":"utc",formatRules:[],pivot});if(!PIVOT_EXPORT_OWNERS.has(grid))PIVOT_EXPORT_OWNERS.set(grid,Symbol());PIVOT_EXPORT_ROWS.set(pivot,{owner:PIVOT_EXPORT_OWNERS.get(grid),rows:snapshot.rows,query:request.query,revision:request.revision,columns,projection:GRID_CALCULATIONS.revision?.(grid.view)});return Object.freeze({request,rows:extra=>grid.exportRows(request,gridExportSignal(options.signal,extra))})};DataGrid.prototype.exportRows=async function*(request,signal){if(!request.pivot){yield*PIVOT_EXPORT_BASE.call(this,request,signal);return}
69
+ const capture=PIVOT_EXPORT_ROWS.get(request.pivot);if(!capture||capture.owner!==PIVOT_EXPORT_OWNERS.get(this)||request.scope!=="filtered"||request.query!==capture.query||request.revision!==capture.revision||
70
+ request.columns!==capture.columns||request.recordCount!==capture.rows.length)throw new Error("CanopyX: pivot table capture ownership differs.");const active=()=>{gridOperationActive(this,signal);if(this.snapshot.revision!==capture.revision||GRID_CALCULATIONS.revision?.(this.view)!==capture.projection)
71
+ throw new Error("CanopyX: pivot table capture revision changed; capture it again.")};for(const row of capture.rows){active();yield row}
72
+ active()};installed.add(context.GRID_PIVOT)}};(global.CanopyXParts||=[]).push(["pivot",part])})(typeof window!=="undefined"?window:globalThis);
@@ -0,0 +1,56 @@
1
+ .cx-plan-legend{display:flex;flex-wrap:wrap;gap:var(--hfx-space-2,0.5rem) var(--hfx-space-4,1rem);padding:calc(var(--hfx-space-1,0.25rem)*1.75) var(--hfx-space-3,0.75rem);border-bottom:var(--hfx-border-width,1px) solid var(--hfx-color-border,light-dark(#e2e4e7,#272a30));font-size:var(--hfx-type-caption-size,0.75rem);color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));}
2
+ [data-cx-calendar-view]:not([data-cx-calendar-view="timeline"]) .cx-plan-legend{display:none;}
3
+ .cx-plan-legend > span{display:inline-flex;align-items:center;gap:var(--hfx-space-1-5,6px);white-space:nowrap;}
4
+ .cx-plan-legend i{display:inline-block;box-sizing:border-box;width:calc(var(--hfx-space-1,0.25rem)*4.5);height:var(--hfx-space-2,0.5rem);}
5
+ .cx-plan-key-critical{border:var(--hfx-border-width,1px) solid var(--hfx-color-status-bad-fg,light-dark(#b8292a,#f47171));border-radius:var(--hfx-radius-control,var(--hfx-radius-md,6px));}
6
+ .cx-plan-key-baseline{border-bottom:calc(var(--hfx-space-1,0.25rem)*0.75) solid var(--hfx-color-text-muted,light-dark(#626770,#8f949d));}
7
+ .cx-plan-key-dependency{border-bottom:var(--hfx-border-width,1px) solid var(--hfx-color-text,light-dark(#16181c,#f2f3f5));position:relative;}
8
+ .cx-plan-key-dependency::after{content:"";position:absolute;right:0;bottom:calc(var(--hfx-space-1,0.25rem)*-0.75);border:solid var(--hfx-color-text,light-dark(#16181c,#f2f3f5));border-width:0 calc(var(--hfx-space-1,0.25rem)*0.25) calc(var(--hfx-space-1,0.25rem)*0.25) 0;padding:var(--hfx-space-0-5,2px);transform:rotate(-45deg);}
9
+ .cx-plan-critical{border-color:var(--hfx-color-status-bad-fg,light-dark(#b8292a,#f47171));outline:var(--hfx-border-width,1px) solid var(--hfx-color-status-bad-fg,light-dark(#b8292a,#f47171));}
10
+ .cx-plan-conflict,.cx-plan-proposal-conflict{outline:var(--hfx-border-width,1px) dashed var(--hfx-color-status-bad-fg,light-dark(#b8292a,#f47171));outline-offset:calc(var(--hfx-space-1,0.25rem)*0.25);}
11
+ .cx-plan .cx-tl-lane .cx-plan-milestone{overflow:visible!important;min-width:calc(var(--hfx-space-1,0.25rem)*3.5)!important;width:calc(var(--hfx-space-1,0.25rem)*3.5)!important;margin-inline-start:calc(var(--hfx-space-1,0.25rem)*-1.75);padding:0;border:0!important;background:transparent!important;box-shadow:none;}
12
+ .cx-plan .cx-tl-lane .cx-plan-milestone::before{content:"";position:absolute;width:var(--hfx-space-3,0.75rem);height:var(--hfx-space-3,0.75rem);margin-top:0;background:var(--hfx-color-accent,light-dark(#11776f,#37c9b8));transform:rotate(45deg);inset-inline-start:calc(var(--hfx-space-1,0.25rem)*0.25);top:calc(50% - var(--hfx-space-1-5,6px));}
13
+ .cx-plan-milestone > *{visibility:hidden;}
14
+ .cx-plan-milestone:focus-visible::before{outline:var(--hfx-border-width,1px) solid var(--hfx-color-text,light-dark(#16181c,#f2f3f5));outline-offset:calc(var(--hfx-space-1,0.25rem)*0.75);}
15
+ .cx-plan-baseline{position:absolute;height:calc(var(--hfx-space-1,0.25rem)*0.75);background:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));border-radius:var(--hfx-radius-control,var(--hfx-radius-md,6px));pointer-events:none;}
16
+ .cx-plan-links{position:absolute;inset:0;pointer-events:none;overflow:hidden;}
17
+ .cx-plan-task > .cx-cal-ev-title{line-height:var(--hfx-type-small-line,1.4);}
18
+ .cx-plan-links path{stroke:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));stroke-width:1.5;fill:none;}
19
+ .cx-plan-links .cx-plan-link-critical{stroke:var(--hfx-color-status-bad-fg,light-dark(#b8292a,#f47171));stroke-width:2;}
20
+ .cx-plan-links .cx-plan-link-conflict{stroke:var(--hfx-color-status-bad-fg,light-dark(#b8292a,#f47171));stroke-dasharray:4 3;}
21
+ .cx-plan-proposal{position:absolute;top:var(--hfx-space-0-5,2px);height:calc(100% - var(--hfx-space-1,0.25rem));box-sizing:border-box;border:var(--hfx-border-width,1px) dashed var(--hfx-color-accent,light-dark(#11776f,#37c9b8));color:var(--hfx-color-text,light-dark(#16181c,#f2f3f5));background:color-mix(in srgb,var(--hfx-color-accent,light-dark(#11776f,#37c9b8)) 18%,var(--hfx-color-surface,light-dark(#ffffff,#16181c)));padding:0 var(--hfx-space-1,0.25rem);overflow:hidden;white-space:nowrap;pointer-events:none;}
22
+ .cx-plan-proposal-invalid{border-color:var(--hfx-color-status-bad-fg,light-dark(#b8292a,#f47171));background:color-mix(in srgb,var(--hfx-color-status-bad-fg,light-dark(#b8292a,#f47171)) 18%,var(--hfx-color-surface,light-dark(#ffffff,#16181c)));}
23
+ .cx-plan-print table{width:100%;border-collapse:collapse;font-size:var(--hfx-type-caption-size,0.75rem);}
24
+ .cx-plan-print th,.cx-plan-print td{padding:calc(var(--hfx-space-1,0.25rem)*1.25);border:var(--hfx-border-width,1px) solid var(--hfx-color-border,light-dark(#e2e4e7,#272a30));text-align:start;overflow-wrap:anywhere;}
25
+ .cx-plan-print caption{text-align:start;font-weight:var(--hfx-weight-semibold,600);padding-bottom:var(--hfx-space-1-5,6px);}
26
+ @media print{.cx-plan-links,.cx-plan-proposal{display:none!important;}.cx-plan-print{display:block!important;}}
27
+ .cx-plan-critical.cx-plan-milestone::before{background:var(--hfx-color-status-bad-fg,light-dark(#b8292a,#f47171));}
28
+ .cx-plan .cx-tl-main{overflow:auto;}
29
+ .cx-plan .cx-tl-now{background:var(--hfx-color-accent,light-dark(#11776f,#37c9b8));}
30
+ .cx-plan-today{position:absolute;inset-block-start:0;transform:translateX(-50%);padding:var(--hfx-space-1,0.25rem) var(--hfx-space-2,0.5rem);border-radius:var(--hfx-radius-control,var(--hfx-radius-md,6px));background:var(--hfx-color-accent,light-dark(#11776f,#37c9b8));color:var(--hfx-color-accent-contrast,light-dark(#ffffff,#0c0d10));font-size:var(--hfx-type-caption-size,0.75rem);font-weight:var(--hfx-weight-semibold,600);}
31
+ .cx-plan-key-conflict{border:var(--hfx-border-width,1px) dashed var(--hfx-color-status-bad-fg,light-dark(#b8292a,#f47171));border-radius:var(--hfx-radius-control,var(--hfx-radius-md,6px));}
32
+ .cx-plan .cx-tl-lane .cx-plan-label-outside{overflow:visible;}
33
+ .cx-plan .cx-tl-lane .cx-plan-label-outside > .cx-cal-ev-title{--cx-plan-label-layer:1;display:block;position:absolute;z-index:var(--cx-plan-label-layer);inset-inline-start:calc(100% + var(--hfx-space-2,0.5rem));max-inline-size:calc(var(--hfx-space-1,0.25rem) * 40);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));background:var(--hfx-color-surface,light-dark(#ffffff,#16181c));font-weight:var(--hfx-weight-semibold,600);visibility:visible;}
34
+ .cx-plan-links path{pointer-events:stroke;stroke:var(--hfx-color-text,light-dark(#16181c,#f2f3f5));stroke-width:2;stroke-linecap:round;stroke-linejoin:round;}
35
+ .cx-plan-links path:hover{stroke:var(--hfx-color-accent,light-dark(#11776f,#37c9b8));stroke-width:3;}
36
+ .cx-plan-agenda{display:none;box-sizing:border-box;height:100%;min-height:0;padding:var(--hfx-space-3,0.75rem);flex-direction:column;}
37
+ .cx-plan-agenda-scroll{flex:1 1 auto;min-height:0;overflow:auto;}
38
+ .cx-plan-agenda-list{display:flow-root;}
39
+ .cx-plan-agenda h3{font-size:var(--hfx-type-body-size,0.875rem);margin:var(--hfx-space-3,0.75rem) 0;font-weight:var(--hfx-weight-semibold,600);}
40
+ .cx-plan-agenda ul{list-style:none;margin:0;padding:0;}
41
+ .cx-plan-agenda li{padding:var(--hfx-space-3,0.75rem);margin-block-end:var(--hfx-space-2,0.5rem);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));}
42
+ .cx-plan-agenda .cx-plan-agenda-title{height:auto;min-height:0;padding:0;border:0;box-shadow:none;background:transparent;color:var(--hfx-color-text,light-dark(#16181c,#f2f3f5));font-weight:var(--hfx-weight-semibold,600);white-space:normal;text-align:start;}
43
+ .cx-plan-agenda p{margin:var(--hfx-space-2,0.5rem) 0 0;font-size:var(--hfx-type-caption-size,0.75rem);line-height:var(--hfx-type-body-line,1.5);}
44
+ .cx-plan-agenda-meta{color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));}
45
+ .cx-plan-agenda-critical{border-inline-start-color:var(--hfx-color-status-bad-fg,light-dark(#b8292a,#f47171))!important;}
46
+ .cx-cal-phone .cx-plan-calendar .cx-plan{display:none;}
47
+ .cx-cal-phone .cx-plan-agenda{display:flex;}
48
+ .cx-cal-phone .cx-plan-calendar .cx-plan-legend{display:none;}
49
+ @media print{.cx-plan-agenda{display:none;}}
50
+ .cx-plan .cx-tl-lane .cx-plan-label-outside > .cx-cal-ev-title{display:block!important;visibility:visible!important;}
51
+ .cx-plan .cx-tl-lane .cx-plan-label-before > .cx-cal-ev-title{inset-inline-start:auto;inset-inline-end:calc(100% + var(--hfx-space-2,0.5rem));}
52
+ .cx-plan-agenda-pager{display:flex;flex:none;align-items:center;justify-content:space-between;gap:var(--hfx-space-2,0.5rem);border-block-start:var(--hfx-border-width,1px) solid var(--hfx-color-border,light-dark(#e2e4e7,#272a30));padding-block-start:var(--hfx-space-2,0.5rem);}
53
+ .cx-plan-agenda [hidden]{display:none;}
54
+ .cx-plan-agenda .cx-plan-resource-toggle{padding:0;border:0;min-height:0;height:auto;background:transparent;font:inherit;color:inherit;}
55
+ .cx-plan-agenda-count{margin-inline-start:var(--hfx-space-2,0.5rem);color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));font-size:var(--hfx-type-caption-size,0.75rem);}
56
+ .cx-plan .cx-tl-lane .cx-plan-task{z-index:auto;}
@@ -0,0 +1,67 @@
1
+ (function(global){"use strict";const installed=new WeakSet();const part={version:"640ca4be173c3310",requires:["timeline"],install(context){if(installed.has(context.DataGrid.prototype))return;const{DataGrid,h,setText,fill,cellValue,cellText,gridFreeze}=context;DataGrid.prototype.validatePlanDefinition=function(decoded){const input=this.view.plan,calendar=this.view.calendar,columns=new Map(this.columns.map(column=>[column.id,column]));const kind=(id,type)=>typeof id==="string"&&columns.get(id)?.kind===type;if(!input||typeof input!=="object"||Array.isArray(input)||!calendar?.resources||!calendar.end||!calendar.resource||
2
+ calendar.recurrence||calendar.sources?.length||calendar.create||calendar.drop||calendar.views?.length&&!calendar.views.includes("timeline")||
3
+ ["criticalPath","proposeMoves","avoidOverlap"].some(key=>input[key]!==undefined&&typeof input[key]!=="boolean")||
4
+ (input.baselineStart===undefined)!==(input.baselineEnd===undefined)||input.baselineStart!==undefined&&
5
+ (!kind(input.baselineStart,"datetime")||!kind(input.baselineEnd,"datetime"))||input.milestone!==undefined&&!kind(input.milestone,"text"))
6
+ throw new Error("CanopyX: a plan needs a resource timeline, paired baseline date columns and valid options.");this.calCanEdit=()=>false;const seen=new Set(),next=new Map(),degree=new Map();if(input.dependencies!==undefined&&(!Array.isArray(input.dependencies)||input.dependencies.length>100000||input.dependencies.some(edge=>{if(!edge||typeof edge.predecessor!=="string"||!edge.predecessor||typeof edge.successor!=="string"||!edge.successor||edge.predecessor===edge.successor||
7
+ edge.lagMinutes!==undefined&&(typeof edge.lagMinutes!=="number"||!Number.isFinite(edge.lagMinutes)||edge.lagMinutes<0))return true;const key=JSON.stringify([edge.predecessor,edge.successor]);if(seen.has(key))return true;seen.add(key);if(!degree.has(edge.predecessor))degree.set(edge.predecessor,0);degree.set(edge.successor,(degree.get(edge.successor)||0)+ 1);if(!next.has(edge.predecessor))next.set(edge.predecessor,[]);next.get(edge.predecessor).push(edge.successor);return false})))throw new Error("CanopyX: a plan needs unique finish-to-start dependencies with nonnegative lag.");const queue=[...degree.keys()].filter(id=>degree.get(id)===0);let visited=0;for(let at=0;at<queue.length;at++){const id=queue[at];visited++;for(const target of next.get(id)||[]){const left=degree.get(target)- 1;degree.set(target,left);if(!left)queue.push(target)}}
8
+ if(visited!==degree.size)throw new Error("CanopyX: plan dependencies cannot contain a cycle.");if(this.view.source.kind==="inline"&&!this.opts.source&&(!this.view.rowData||decoded)){const rows=new Set(this.view.rows.map(row=>row.id));for(const id of degree.keys())if(!rows.has(id))throw new Error("CanopyX: a plan dependency names an unknown task: "+id)}};function planInstant(value){value=cellValue(value);return typeof value==="string"&&value.trim()?Date.parse(value):NaN}
9
+ function planMilestone(grid,task){const value=grid.view.plan.milestone&&cellValue(task.row.cells[grid.view.plan.milestone]);return task.end===task.start||value===true||value==="true"}
10
+ function planNetwork(tasks,dependencies){const nodes=new Map(),edges=[],missing=new Set();for(const task of tasks)if(!task.background)nodes.set(task.key,{task,before:[],after:[],degree:0,early:0,late:Infinity});for(const input of dependencies){const from=nodes.get(input.predecessor),to=nodes.get(input.successor),lag=(input.lagMinutes||0)*60000;if(!from||!to){if(!from)missing.add(input.predecessor);if(!to)missing.add(input.successor);continue}
11
+ const edge={from,to,lag,conflict:from.task.end+lag>to.task.start};from.after.push(edge);to.before.push(edge);to.degree++;edges.push(edge)}
12
+ const queue=[...nodes.values()].filter(node=>!node.degree),order=[];let finish=0;for(let at=0;at<queue.length;at++){const node=queue[at],duration=node.task.end-node.task.start;order.push(node);finish=Math.max(finish,node.early+duration);for(const edge of node.after){edge.to.early=Math.max(edge.to.early,node.early+duration+edge.lag);if(!--edge.to.degree)queue.push(edge.to)}}
13
+ const complete=missing.size===0&&order.length===nodes.size;if(complete)for(let at=order.length-1;at>=0;at--){const node=order[at],duration=node.task.end-node.task.start;node.late=node.after.length?node.after.reduce((least,edge)=>Math.min(least,edge.to.late-edge.lag),Infinity)- duration:finish-duration;node.critical=Math.abs(node.late-node.early)<1}
14
+ return{nodes,edges,complete,missing:[...missing],cycle:order.length!==nodes.size}}
15
+ Object.assign(DataGrid.prototype,{planAnalyze(){if(!this.view.plan)return null;const cache=this.planAnalysis;if(cache?.version===this.calVersion&&cache.events===this.calEvents&&cache.rows===this.view.rows)return cache.network;const inline=this.view.source.kind==="inline"&&!this.opts.source;const tasks=(inline?this.calEdited(this.view.rows).map(row=>this.calKit.calBase(this,row)).filter(Boolean):this.calEvents)
16
+ .map(task=>planMilestone(this,task)?{...task,end:task.start}:task);const network=planNetwork(tasks,this.view.plan.dependencies||[]);if(!inline||tasks.length!==this.view.rows.length&&inline)network.complete=false;this.planAnalysis={version:this.calVersion,events:this.calEvents,rows:this.view.rows,network};return network},planText(name,values){const text=this.labels[name]||this.calLabels?.[name]||PLAN_LABELS[name];return values?fill(text,values):text},planProblems(task,proposal){const network=this.planAnalyze(),problems=[];const add=(kind,other,label)=>problems.push({kind,id:other?.key??null,label});if(!Number.isFinite(proposal.start)||!Number.isFinite(proposal.end)||proposal.end<proposal.start)
17
+ add("range",null,this.planText("planInvalidRange"));if(!this.calAllowsSpan(proposal.start,proposal.end,task.allDay))add("range",null,this.calLabels.calendarOutsideRange);if(proposal.resource!=null&&(!this.tl.names.has(proposal.resource)||this.tl.children.has(proposal.resource)))
18
+ add("resource",null,this.planText("planInvalidResource"));const node=network.nodes.get(task.key);if(!network.complete)add("incomplete",null,this.planText("planIncomplete"));for(const edge of node?.before||[])if(proposal.start<edge.from.task.end+edge.lag)
19
+ add("dependency",edge.from.task,this.planText("planDependencyConflict",{task:edge.from.task.title}));for(const edge of node?.after||[])if(proposal.end+edge.lag>edge.to.task.start)
20
+ add("dependency",edge.to.task,this.planText("planDependencyConflict",{task:edge.to.task.title}));const overlap=(start,end)=>this.calKit.calOverlaps(start,end,proposal.start,proposal.end)||
21
+ this.calKit.calOverlaps(proposal.start,proposal.end,start,end);if(this.view.plan.avoidOverlap!==false)for(const other of network.nodes.values()){const item=other.task;if(item.key!==task.key&&item.resource===proposal.resource&&overlap(item.start,item.end))
22
+ add("overlap",item,this.planText("planOverlapConflict",{task:item.title}))}
23
+ for(const range of this.calRanges)if(range.kind==="unavailable"&&(range.resource==null||range.resource===proposal.resource)&&overlap(range.start,range.end))
24
+ add("unavailable",null,this.calLabels.calendarUnavailable);return problems}});const PLAN_METRICS=Object.freeze({actualMinHeight:20,actualBaselineGap:16,baselineMinWidth:3,baselineBottom:7});function planTaskTemplate(element,event,node,labels,milestone,critical,proposeMoves){element.classList.add("cx-plan-task");element.classList.toggle("cx-plan-milestone",milestone);element.classList.toggle("cx-plan-critical",!!critical);element.classList.toggle("cx-plan-conflict",!!node&&[...node.before,...node.after].some(edge=>edge.conflict));if(milestone)labels.push(this.planText("planMilestone"));if(critical)labels.push(this.planText("planCritical"));for(const edge of node?.before||[])if(edge.conflict)labels.push(this.planText("planDependencyConflict",{task:edge.from.task.title}));if(proposeMoves){element.setAttribute("aria-keyshortcuts","Alt+ArrowLeft Alt+ArrowRight Alt+ArrowUp Alt+ArrowDown");labels.push(this.planText("planMoveHint"))}
25
+ element.setAttribute("aria-label",labels.join(", "));element.title=labels.join(", ")}
26
+ function planLegendTemplate(){const legend=h("div","cx-plan-legend");for(const[kind,label]of[["critical","planCritical"],["baseline","planBaselineLabel"],["dependency","planDependencies"]]){const item=h("span"),mark=h("i","cx-plan-key-"+kind);mark.setAttribute("aria-hidden","true");item.append(mark,document.createTextNode(this.planText(label)));legend.appendChild(item)}
27
+ const conflict=h("span"),mark=h("i","cx-plan-key-conflict");mark.setAttribute("aria-hidden","true");conflict.append(mark,document.createTextNode(this.planText("planConflict")));legend.appendChild(conflict);return legend}
28
+ function planBaselineTemplate(element,lane,axis,from,to,text){const line=h("div","cx-plan-baseline");line.style.insetInlineStart=axis.x(from)+ "px";line.style.width=Math.max(PLAN_METRICS.baselineMinWidth,axis.x(to)- axis.x(from))+ "px";line.style.top=(Number.parseFloat(element.style.top)+ element._planHeight-PLAN_METRICS.baselineBottom)+ "px";line.title=text;line.setAttribute("aria-hidden","true");lane.appendChild(line)}
29
+ function planLinksTemplate(axis,height){const svg=document.createElementNS("http://www.w3.org/2000/svg","svg");svg.classList.add("cx-plan-links");svg.setAttribute("aria-hidden","true");svg.style.width=axis.total+"px";svg.style.height=height;return svg}
30
+ function planLinkTemplate(svg,axis,from,to,edge,critical,text){const mirror=x=>this.calRtl?axis.total-x:x,x1=mirror(from.end),x2=mirror(to.start),sign=this.calRtl?-1:1;const exit=x1-5*sign,entry=x2-10*sign;const edgeY=to.y>=from.y?from.bottom:from.top,corridor=edgeY +(to.y>=from.y?4:-4);const path=document.createElementNS(svg.namespaceURI,"path");const flat=Math.abs(from.y-to.y)<1&&(x2-x1)*sign>=-1;const route=flat?"M"+(Math.abs(x1-x2)<1?x1-5*sign:x1)+","+from.y+" H"+x2:"M"+exit+","+edgeY+" V"+corridor+" H"+entry+" V"+to.y+" H"+x2;const lead=x2-5*sign;const arrow=" M"+lead+","+(to.y-4)+" L"+x2+","+to.y+" L"+lead+","+(to.y+4);path.setAttribute("d",route+arrow);path.classList.toggle("cx-plan-link-conflict",edge.conflict);path.classList.toggle("cx-plan-link-critical",critical);const title=document.createElementNS(svg.namespaceURI,"title");title.textContent=text;path.appendChild(title);svg.appendChild(path)}
31
+ function planAgendaTemplate(labels){const root=h("div","cx-plan-agenda"),scroll=h("div","cx-plan-agenda-scroll"),list=h("div","cx-plan-agenda-list");const pager=h("nav","cx-plan-agenda-pager"),previous=h("button","cx-btn cx-plan-agenda-previous"),next=h("button","cx-btn cx-plan-agenda-next"),position=h("span","cx-plan-agenda-position");previous.type=next.type="button";previous.setAttribute("aria-label",labels.gridPreviousPage);next.setAttribute("aria-label",labels.gridNextPage);setText(previous,"\u2039");setText(next,"\u203a");pager.setAttribute("aria-label",labels.gridPaging);pager.append(previous,position,next);scroll.appendChild(list);root.append(scroll,pager);return{root,scroll,list,pager,previous,next,position,page:0}}
32
+ function planAgendaPageTemplate(items,moves){const fragment=document.createDocumentFragment();let current=null,list=null;const heading=group=>{const section=h("section"),title=h("h3"),count=h("span","cx-plan-agenda-count");if(group.expandable){const button=h("button","cx-btn cx-plan-resource-toggle"),mark=h("span");button.type="button";mark.setAttribute("aria-hidden","true");setText(mark,group.row._open?"\u25be":"\u25b8");button.append(mark,document.createTextNode(group.label));button.dataset.cxPlanToggle=group.id;button.setAttribute("aria-expanded",String(!!group.row._open));title.appendChild(button)}else setText(title,group.label);setText(count,String(group.items.length));title.appendChild(count);list=h("ul");list.setAttribute("aria-label",group.label);section.append(title,list);fragment.appendChild(section);current=group};for(const item of items){if(item.kind==="resource"){heading(item.group);continue}
33
+ if(current!==item.group)heading(item.group);const row=h("li"),button=h("button","cx-btn cx-cal-ev cx-plan-agenda-title"),dates=h("p"),meta=h("p","cx-plan-agenda-meta");button.type="button";setText(button,item.task.title);button.title=item.task.title;button.dataset.cxPlanRecord=item.task.key;button.dataset.cxPlanResource=item.group.id;button.dataset.cxEvent=item.task.id;button.dataset.cxRecord=item.task.key;button._calEvent={...item.task,resource:item.group.id};button.setAttribute("aria-label",[item.task.title,item.fullDates,item.meta].join(", "));if(moves)button.setAttribute("aria-keyshortcuts","Alt+ArrowLeft Alt+ArrowRight Alt+ArrowUp Alt+ArrowDown");setText(dates,item.dates);setText(meta,item.meta);row.classList.toggle("cx-plan-agenda-critical",item.critical);row.append(button,dates,meta);list.appendChild(row)}
34
+ return fragment}
35
+ const PLAN_LABELS={planMilestone:"Milestone",planCritical:"Critical path",planBaseline:"Baseline: {start} – {end}",planDependency:"{from} finishes before {to} starts",planDependencyConflict:"Dependency conflict with {task}",planOverlapConflict:"Overlaps {task}",planIncomplete:"The complete task network is unavailable; the host must check this proposal.",planInvalidRange:"The task must finish at or after its start.",planInvalidResource:"Choose a task resource.",planProposal:"Proposed {task}: {start} – {end}",planMoveHint:"Alt+Left or Alt+Right proposes a new date; Alt+Up or Alt+Down proposes a resource.",planConflict:"Conflict",planActual:"Actual",planPending:"Proposal awaiting the host",planBaselineLabel:"Baseline",planDependencies:"Dependencies"};const PLAN_EVENT=DataGrid.prototype.calEventElement;DataGrid.prototype.calEventElement=function(event,kind){const element=PLAN_EVENT.call(this,event,kind);if(this.view.plan&&kind==="lane"){this.planDecorate(element,event);this.planStart()}
36
+ return element};const PLAN_LANES=DataGrid.prototype.tlPaintLanes;DataGrid.prototype.tlPaintLanes=function(...args){const result=PLAN_LANES.apply(this,args);if(this.view.plan&&this.tlCanvas&&this.calView==="timeline")this.planPaint();return result};Object.assign(DataGrid.prototype,{planDecorate(element,event){const network=this.planAnalyze(),node=network.nodes.get(event.key),labels=[this.calEventName(event)];const milestone=planMilestone(this,event),critical=this.view.plan.criticalPath!==false&&network.complete&&node?.critical;planTaskTemplate.call(this,element,event,node,labels,milestone,critical,this.view.plan.proposeMoves)},planPaint(){this.tlRoot.classList.add("cx-plan");this.host.classList.add("cx-plan-host");this.calRoot.classList.add("cx-plan-calendar");if(!this.planLegend){const legend=this.planLegend=planLegendTemplate.call(this);this.calBody.before(legend);this.lifetime.signal.addEventListener("abort",()=>{legend.remove();this.host.classList.remove("cx-plan-host");if(this.planFit&&this.host.style.maxHeight===this.planFit.written)this.host.style.maxHeight=this.planFit.inline;this.planAgendaModel=this.planAgendaState=null},{once:true})}
37
+ const network=this.planAnalyze(),axis=this.tlAxis,positions=new Map(),canvasBox=this.tlCanvas.getBoundingClientRect();for(const lane of this.tlLanes.values()){for(const previous of lane.querySelectorAll(".cx-plan-baseline"))previous.remove();for(const element of lane.querySelectorAll(".cx-plan-task")){const task=element._calEvent;if(planMilestone(this,task))element.style.width="0px";const spec=this.view.plan;if(spec.baselineStart){element._planHeight??=Number.parseFloat(element.style.height);if(!planMilestone(this,task))element.style.height=Math.max(PLAN_METRICS.actualMinHeight,element._planHeight-PLAN_METRICS.actualBaselineGap)+ "px"}
38
+ const rect=element.getBoundingClientRect(),start=axis.x(task.tlStart),end=axis.x(planMilestone(this,task)?task.tlStart:task.tlEnd);if(!positions.has(task.key))positions.set(task.key,{start,end,y:rect.top-canvasBox.top+rect.height / 2, top: rect.top - canvasBox.top, bottom: rect.bottom - canvasBox.top });
39
+ const title=element.querySelector(".cx-cal-ev-title");if(title){title.title=task.title;element.classList.remove("cx-plan-label-before");const outside=title.scrollWidth>element.clientWidth-12||planMilestone(this,task);element.classList.toggle("cx-plan-label-outside",outside);const box=title.getBoundingClientRect(),x=axis.x(Date.now()),today=canvasBox.left+(this.calRtl?axis.total-x:x);element.classList.toggle("cx-plan-label-before",outside&&this.cal.now&&today>=box.left-4&&today<=box.right+4)}
40
+ if(!spec.baselineStart||!spec.baselineEnd)continue;const from=planInstant(task.row.cells[spec.baselineStart]),to=planInstant(task.row.cells[spec.baselineEnd]);if(!Number.isFinite(from)||!Number.isFinite(to)||to<=from||from>=axis.to||to<axis.from)continue;const text=this.planText("planBaseline",{start:this.calFmt.longDay(this.calZone.day(from)),end:this.calFmt.longDay(this.calZone.day(to))});planBaselineTemplate(element,lane,axis,from,to,text);element._planName||=element.getAttribute("aria-label");element.setAttribute("aria-label",element._planName+"; "+text)}}
41
+ for(const lane of this.tlLanes.values()){const rows=new Map();for(const element of lane.querySelectorAll(".cx-plan-task")){const key=element.style.top;if(!rows.has(key))rows.set(key,[]);rows.get(key).push({element,title:element.querySelector(".cx-cal-ev-title"),box:element.getBoundingClientRect()})}
42
+ const overlaps=(a,b)=>a.left<b.right&&a.right>b.left&&a.top<b.bottom&&a.bottom>b.top;for(const items of rows.values()){items.sort((a,b)=>a.box.left-b.box.left);for(const[index,item]of items.entries()){if(!item.title||!item.element.classList.contains("cx-plan-label-outside")||item.element.classList.contains("cx-plan-milestone"))continue;const box=item.title.getBoundingClientRect();if([items[index-1],items[index+1]].some(other=>other&&(overlaps(box,other.box)||other.title&&overlaps(box,other.title.getBoundingClientRect()))))
43
+ item.element.classList.remove("cx-plan-label-outside","cx-plan-label-before")}}}
44
+ const svg=planLinksTemplate(axis,this.tlCanvas.style.height);for(const edge of network.edges){const from=positions.get(edge.from.task.key),to=positions.get(edge.to.task.key);if(!from||!to)continue;const tight=Math.abs(edge.from.early+edge.from.task.end-edge.from.task.start+edge.lag-edge.to.early)<1;const critical=this.view.plan.criticalPath!==false&&network.complete&&edge.from.critical&&edge.to.critical&&tight;planLinkTemplate.call(this,svg,axis,from,to,edge,critical,this.planText("planDependency",{from:edge.from.task.title,to:edge.to.task.title}))}
45
+ this.planLinks?.remove();this.planLinks=svg;this.tlCanvas.appendChild(svg);this.tlRoot.dataset.cxPlanComplete=String(network.complete);if(!this.calPhone)this.planFitHeight((this.tlTree.tops[this.tlTree.rows.length]||0)+this.tlHead.offsetHeight+2);this.tlHead.querySelector(".cx-plan-today")?.remove();const now=Date.now();if(this.cal.now&&now>=axis.from&&now<axis.to){const label=h("span","cx-plan-today");setText(label,this.calLabels.calendarToday);label.style.insetInlineStart=axis.x(now)+ "px";this.tlHead.appendChild(label)}
46
+ this.planPaintAgenda(network);this.planPaintProposal()}});const PLAN_AGENDA_PAGE=50;Object.assign(DataGrid.prototype,{planPaintAgenda(network){if(!this.calPhone){this.planAgenda?.remove();this.planAgenda=this.planAgendaState=this.planAgendaModel=null;return}
47
+ let state=this.planAgendaState;if(!state){state=this.planAgendaState=planAgendaTemplate(this.labels);this.planAgenda=state.root;state.previous.addEventListener("click",()=>{--state.page;this.planPaintAgendaPage()});state.next.addEventListener("click",()=>{++state.page;this.planPaintAgendaPage()})}
48
+ if(state.root.parentNode!==this.calBody)this.calBody.appendChild(state.root);const old=this.planAgendaModel;if(old?.events===this.calEvents&&old.rows===this.tlTree.rows&&old.network===network){this.planFitAgendaHeight();return}
49
+ const groups=this.tlTree.rows.filter(row=>!row.status).map(row=>({id:row.key,label:this.calResourceName(row.key),row,expandable:this.tlTree.expandable(row),items:[]})),index=new Map(groups.map(group=>[group.id,group]));for(const task of this.calEvents){if(task.background)continue;const node=network.nodes.get(task.key),critical=this.view.plan.criticalPath!==false&&network.complete&&node?.critical,milestone=planMilestone(this,task);const first=this.calZone.day(task.start),last=this.calZone.day(task.end),start=this.calFmt.longDay(first),fullDates=milestone?start:start+" – "+this.calFmt.longDay(last);const dates=milestone?this.calFmt.date(first):this.calFmt.dayRange(first,last);const meta=[this.planText("planDependencies")+": "+(node?.before.length||0),critical?this.planText("planCritical"):null,node&&[...node.before,...node.after].some(edge=>edge.conflict)?this.planText("planConflict"):null,milestone?this.planText("planMilestone"):null].filter(Boolean).join(" · ");for(const id of this.calEventResources(task)){const group=index.get(id);if(group)group.items.push({kind:"task",group,task,dates,fullDates,meta,critical})}}
50
+ const items=groups.flatMap(group=>[{kind:"resource",group},...group.items]);this.planAgendaModel={events:this.calEvents,rows:this.tlTree.rows,network,items,index};state.page=old?.events===this.calEvents?Math.min(state.page,Math.max(0,Math.ceil(items.length/PLAN_AGENDA_PAGE)-1)):0;
51
+ this.planStart();this.planPaintAgendaPage()},planPaintAgendaPage(){const state=this.planAgendaState,model=this.planAgendaModel;if(!state||!model)return;const active=state.list.contains(document.activeElement)?document.activeElement:null,record=active?.dataset.cxPlanRecord,resource=active?.dataset.cxPlanResource,toggle=active?.dataset.cxPlanToggle;const pages=Math.max(1,Math.ceil(model.items.length/PLAN_AGENDA_PAGE));state.page=Math.max(0,Math.min(pages-1,state.page));
52
+ state.list.replaceChildren(planAgendaPageTemplate(model.items.slice(state.page*PLAN_AGENDA_PAGE,(state.page+1)*PLAN_AGENDA_PAGE),this.view.plan.proposeMoves));for(const button of state.list.querySelectorAll("[data-cx-plan-toggle]"))button.addEventListener("click",()=>{const row=model.index.get(button.dataset.cxPlanToggle)?.row;this.tlTree.toggle(row).catch(error=>this.emit("error",{error}))});state.previous.disabled=state.page===0;state.next.disabled=state.page===pages-1;state.pager.hidden=pages===1;setText(state.position,this.labels.gridPageNumber+" "+(state.page+1)+" / "+pages);state.scroll.scrollTop=0;const target=[...state.list.querySelectorAll("button")].find(button=>record?button.dataset.cxPlanRecord===record&&button.dataset.cxPlanResource===resource:toggle&&button.dataset.cxPlanToggle===toggle);if(target)target.focus({preventScroll:true});this.planFitAgendaHeight()},planFitAgendaHeight(){const state=this.planAgendaState;if(!state)return;const css=getComputedStyle(state.root),padding=parseFloat(css.paddingTop)+parseFloat(css.paddingBottom);this.planFitHeight(state.list.getBoundingClientRect().height+padding+(state.pager.hidden?0:state.pager.getBoundingClientRect().height))}});Object.assign(DataGrid.prototype,{planFitHeight(content){if(this.view.autoHeight===false||!this.host.isConnected)return;const style=this.host.style;if(!this.planFit){const own=this.fitInline!==undefined?this.fitMax:getComputedStyle(this.host).maxHeight;this.planFit={inline:this.fitInline??style.maxHeight,bound:typeof own==="number"?own:own==="none"?Infinity:/^[\d.]+px$/.test(own)?parseFloat(own):own};
53
+ }
54
+ const inflow=(parent,omit)=>[...parent.children].filter(child=>child!==omit).reduce((sum,child)=>{const css=getComputedStyle(child);return["absolute","fixed"].includes(css.position)?sum:sum+child.getBoundingClientRect().height},0);const css=getComputedStyle(this.host),border=["paddingTop","paddingBottom","borderTopWidth","borderBottomWidth"].reduce((sum,key)=>sum+(parseFloat(css[key])||0),0);const height=Math.ceil(border+inflow(this.host,this.calRoot)+inflow(this.calRoot,this.calBody)+content),bound=this.planFit.bound;const value=typeof bound==="number"?Math.min(bound,height)+"px":"min("+height+"px, "+bound+")";this.planFit.written=value;if(style.maxHeight!==value)style.maxHeight=value}});Object.assign(DataGrid.prototype,{planStart(){if(this.planStarted)return;this.planStarted=true;if(!this.view.plan.proposeMoves)return;const signal=this.lifetime.signal,body=this.calBody;body.addEventListener("pointerdown",event=>{const button=event.target.closest(".cx-plan-task"),task=button?._calEvent;if(event.button!==0||!task||task.base?.external)return;event.stopImmediatePropagation();if(this.planSending)return;this.planDrag={pointer:event.pointerId,task,x:event.clientX,y:event.clientY,version:this.calVersion};body.setPointerCapture(event.pointerId)},{capture:true,signal});body.addEventListener("pointermove",event=>{const drag=this.planDrag;if(!drag||drag.pointer!==event.pointerId)return;event.stopImmediatePropagation();if(!drag.moved&&Math.hypot(event.clientX-drag.x,event.clientY-drag.y)<5)return;drag.moved=true;event.preventDefault();const axis=this.tlAxis,rect=this.tlCanvas.getBoundingClientRect(),x=this.calRtl?rect.right-event.clientX:event.clientX-rect.left;const originX=this.calRtl?rect.right-drag.x:drag.x-rect.left;let delta=axis.time(x)- axis.time(originX);if(axis.zoom==="hour"){const step=(this.cal.slotMinutes||30)*60000;delta=Math.round(delta / step) * step;
55
+ }else{const days=this.calZone.day(axis.time(x))- this.calZone.day(axis.time(originX));delta=this.calZone.instant(this.calZone.day(drag.task.start)+ days,this.calZone.parts(drag.task.start).minutes)- drag.task.start}
56
+ const lane=document.elementFromPoint(event.clientX,event.clientY)?.closest(".cx-tl-lane[data-cx-resource]");const proposal={start:drag.task.start+delta,end:(planMilestone(this,drag.task)?drag.task.start:drag.task.end)+ delta,resource:lane?.dataset.cxResource??drag.task.resource};this.planPreview={task:drag.task,proposal,conflicts:this.planProblems(drag.task,proposal)};this.planPaintProposal()},{capture:true,signal});const finish=(event,canceled)=>{const drag=this.planDrag;if(!drag||drag.pointer!==event.pointerId)return;event.stopImmediatePropagation();this.planDrag=null;if(body.hasPointerCapture(event.pointerId))body.releasePointerCapture(event.pointerId);const preview=this.planPreview;if(drag.moved){this.planSuppressClick=true;queueMicrotask(()=>{setTimeout(()=>{this.planSuppressClick=false},0)})}
57
+ if(canceled||drag.version!==this.calVersion||!drag.moved){this.planClearProposal();return}
58
+ if(preview)void this.planSendProposal(drag.task,preview.proposal)};body.addEventListener("pointerup",event=>finish(event,false),{capture:true,signal});body.addEventListener("pointercancel",event=>finish(event,true),{capture:true,signal});body.addEventListener("lostpointercapture",event=>{if(this.planDrag?.pointer===event.pointerId){this.planDrag=null;this.planClearProposal()}},{signal});body.addEventListener("click",event=>{if(this.planSuppressClick){event.preventDefault();event.stopImmediatePropagation();this.planSuppressClick=false}},{capture:true,signal});body.addEventListener("keydown",event=>{if(event.key==="Escape"&&(this.planPreview||this.planDrag)){event.stopImmediatePropagation();this.planDrag=null;this.planClearProposal();return}
59
+ const task=event.target.closest(".cx-plan-task,.cx-plan-agenda-title")?._calEvent;if(!task||!event.altKey||!["ArrowLeft","ArrowRight","ArrowUp","ArrowDown"].includes(event.key))return;event.preventDefault();event.stopImmediatePropagation();const horizontal=["ArrowLeft","ArrowRight"].includes(event.key),sign=(event.key==="ArrowRight"||event.key==="ArrowDown"?1:-1)*(horizontal&&this.calRtl?-1:1);const delta=horizontal?this.calZoom==="hour"?sign*(this.cal.slotMinutes||30)*60000:
60
+ this.calZone.instant(this.calZone.day(task.start)+ sign,this.calZone.parts(task.start).minutes)- task.start:0;void this.planSendProposal(task,{start:task.start+delta,end:task.end+delta,resource:horizontal?task.resource:this.calResourceStep(task.resource,sign)})},{capture:true,signal});const reset=()=>{this.planDrag=null;this.planClearProposal()};for(const name of["query","rowsChanged","commit","cellValueChanged"])this.host.addEventListener("canopyx:"+name,reset,{signal});signal.addEventListener("abort",()=>{this.planDrag=null;this.planClearProposal()},{once:true})},planClearProposal(){this.planPreview=null;this.planGhost?.remove();this.planGhost=null;for(const item of this.tlCanvas?.querySelectorAll(".cx-plan-proposal-conflict")||[])item.classList.remove("cx-plan-proposal-conflict")},planPaintProposal(){this.planGhost?.remove();this.planGhost=null;for(const item of this.tlCanvas?.querySelectorAll(".cx-plan-proposal-conflict")||[])item.classList.remove("cx-plan-proposal-conflict");const preview=this.planPreview;if(!preview)return;const{task,proposal,conflicts}=preview,lane=this.tlLanes.get(proposal.resource);const ids=new Set(conflicts.map(problem=>problem.id).filter(Boolean));for(const element of this.tlCanvas.querySelectorAll(".cx-plan-task"))element.classList.toggle("cx-plan-proposal-conflict",ids.has(element.dataset.cxRecord));if(!lane)return;const ghost=this.planGhost=h("div","cx-plan-proposal" +(conflicts.length?" cx-plan-proposal-invalid":""));ghost.style.insetInlineStart=this.tlAxis.x(proposal.start)+ "px";ghost.style.width=Math.max(8,this.tlAxis.x(proposal.end)- this.tlAxis.x(proposal.start))+ "px";ghost.textContent=task.title;ghost.title=conflicts.map(item=>item.label).join("; ")||this.planText("planPending");ghost.setAttribute("aria-hidden","true");lane.appendChild(ghost)},async planSendProposal(task,proposal){if(this.destroyed||this.planSending||!this.view.plan.proposeMoves)return false;if(planMilestone(this,task))proposal={...proposal,end:proposal.start};const conflicts=this.planProblems(task,proposal),version=this.calVersion;const detail=gridFreeze({id:task.key,previous:{start:new Date(task.start).toISOString(),end:new Date(task.end).toISOString(),resource:task.resource},proposed:{start:new Date(proposal.start).toISOString(),end:new Date(proposal.end).toISOString(),resource:proposal.resource},conflicts:JSON.parse(JSON.stringify(conflicts))});this.planPreview={task,proposal,conflicts};this.planPaintProposal();const message=this.planText("planProposal",{task:task.title,start:detail.proposed.start,end:detail.proposed.end});this.calAnnounce(message +(conflicts.length?". "+conflicts.map(item=>item.label).join("; "):". "+this.planText("planPending")));if(!this.emit("planChangeProposal",detail)){this.planClearProposal();return false}
61
+ const handler=this.opts.plan?.propose;if(typeof handler!=="function"){this.planClearProposal();return false}
62
+ this.planSending=true;try{await this.runGridHook(signal=>handler(detail,{signal}),this.dataLifetime.signal);return!this.destroyed&&this.calVersion===version}
63
+ catch(error){if(!this.destroyed)this.emit("error",{error});return false}
64
+ finally{this.planSending=false;this.planClearProposal()}}});const PLAN_PRINT=DataGrid.prototype.tlPrint;DataGrid.prototype.tlPrint=function(on){if(!this.view.plan)return PLAN_PRINT.call(this,on);this.tlPrintView?.remove();this.tlPrintView=null;if(!on||this.calView!=="timeline"||!this.tlTree)return;const view=this.tlPrintView=h("div","cx-tl-print cx-plan-print"),network=this.planAnalyze();const table=h("table"),caption=h("caption");caption.textContent=this.view.label;table.appendChild(caption);const head=h("tr");for(const label of[this.cal.title.title,this.calLabels.calendarResources,this.planText("planActual"),this.planText("planBaselineLabel"),this.planText("planCritical"),this.planText("planDependencies")]){const cell=h("th");cell.textContent=label;head.appendChild(cell)}
65
+ const thead=h("thead");thead.appendChild(head);table.appendChild(thead);const body=h("tbody"),ids=new Set(this.tlTree.rows.filter(row=>!row.status).map(row=>row.key));for(const node of network.nodes.values()){const task=node.task;if(!ids.has(task.resource))continue;const row=h("tr"),spec=this.view.plan,date=value=>Number.isFinite(value)?new Date(value).toISOString():"";const baseline=spec.baselineStart?date(planInstant(task.row.cells[spec.baselineStart]))+ " – "+date(planInstant(task.row.cells[spec.baselineEnd])):"";const dependencies=node.before.map(edge=>edge.conflict?this.planText("planDependencyConflict",{task:edge.from.task.title}):edge.from.task.title).join(", ");const values=[(planMilestone(this,task)?"◆ ":"")+ task.title +(planMilestone(this,task)?" ("+this.planText("planMilestone")+ ")":""),this.calResourceName(task.resource),date(task.start)+ " – "+date(task.end),baseline,network.complete?this.view.plan.criticalPath!==false&&node.critical?this.planText("planCritical"):"":this.planText("planIncomplete"),dependencies];for(const value of values){const cell=h("td");cell.textContent=value;row.appendChild(cell)}
66
+ if(node.before.some(edge=>edge.conflict))row.classList.add("cx-plan-conflict");body.appendChild(row)}
67
+ table.appendChild(body);view.appendChild(table);this.tlRoot.appendChild(view)};installed.add(context.DataGrid.prototype)}};(global.CanopyXParts||=[]).push(["plan",part])})(typeof window!=="undefined"?window:globalThis);
@@ -1,7 +1,7 @@
1
- (function(global){"use strict";const installed=new WeakSet();const part={version:"4337e3756b443220",install(context){if(installed.has(context.GRID_PRESENTER_IMPL))return;const{GRID_PRESENTER_IMPL,h,setText,fill,cellValue,cellText,isCellObject,columnNumericTone,columnValueTone,gridPresenterTone,gridPresenterLink,numberFormat,toneColors,toneOf,applyTone,gridIcon,badgeElement,GRID_HISTORY_KINDS,GRID_HISTORY_IMPL,RECORD_IMPL}=context;function gridRenderPresenter(grid,column,options,record,complete){return(row,element)=>{const cell=row.cells[column.id],raw=cellValue(cell),text=cellText(cell,column,grid.fmt),object=isCellObject(cell);const rule=columnNumericTone(column,raw),declaredTone=columnValueTone(column,cell),tone=declaredTone||gridPresenterTone(options,raw,object?cell.tone:null);element.classList.add("cx-presented-cell");if(rule||object&&cell.title)element.title=rule?.label||cell.title;if(declaredTone)applyTone(element,element.className,toneOf(declaredTone));if(rule)element.setAttribute("aria-description",rule.label);const add=(value,cls="cx-presenter-text",tag="span")=>{const item=h(tag,cls),textItem=/^(cx-presenter-text|p-mono|p-link|cx-wrapped-text|p-muted)\b/.test(cls);setText(item,value);
1
+ (function(global){"use strict";const installed=new WeakSet();const part={version:"d5f9d61bb730974b",install(context){if(installed.has(context.GRID_PRESENTER_IMPL))return;const{GRID_PRESENTER_IMPL,h,setText,fill,cellValue,cellText,changeCellText,isCellObject,columnNumericTone,columnValueTone,gridPresenterTone,gridPresenterLink,numberFormat,toneColors,toneOf,applyTone,gridIcon,badgeElement,GRID_HISTORY_KINDS,GRID_HISTORY_IMPL,RECORD_IMPL}=context;function gridRenderPresenter(grid,column,options,record,complete){return(row,element)=>{const cell=row.cells[column.id],raw=cellValue(cell),text=cellText(cell,column,grid.fmt),object=isCellObject(cell);const rule=columnNumericTone(column,raw),declaredTone=columnValueTone(column,cell),tone=declaredTone||gridPresenterTone(options,raw,object?cell.tone:null);element.classList.add("cx-presented-cell");if(rule||object&&cell.title)element.title=rule?.label||cell.title;if(declaredTone)applyTone(element,element.className,toneOf(declaredTone));if(rule)element.setAttribute("aria-description",rule.label);const add=(value,cls="cx-presenter-text",tag="span")=>{const item=h(tag,cls),textItem=/^(cx-presenter-text|p-mono|p-link|cx-wrapped-text|p-muted)\b/.test(cls);setText(item,value);
2
2
  if(declaredTone&&textItem)item.style.color="inherit";if(column.wrap&&textItem){item.classList.add("cx-wrapped-text");item.style.maxWidth="100%";item.style.flex="none";item.title=rule?.label||String(value);if(complete){item.style.webkitLineClamp="unset";item.style.maxHeight="none"}
3
3
  else{const lines=record?column.maxLines??2:grid.layoutMode==="cards"?Math.min(column.maxLines??2,2):grid.autoRows?column.maxLines??2:1;item.style.webkitLineClamp=String(lines);item.style.maxHeight=lines*1.4+"em"}}
4
- element.appendChild(item);return item};const more=(value,label=grid.labels.gridShowMore)=>{const button=add(label,"cx-link cx-presenter-expand","button");button.type="button";button.setAttribute("aria-haspopup","dialog");button.setAttribute("aria-label",fill(grid.labels.gridExpandCell,{column:column.title}));button.addEventListener("click",()=>grid.showReportingText(column,value,button,row.id,options.mono));return button};if(GRID_HISTORY_KINDS.includes(options.kind)){if(GRID_HISTORY_IMPL.render)GRID_HISTORY_IMPL.render(grid,column,options,cell,element,text);else grid.loadHistory()}else if(options.kind==="structured"){if(RECORD_IMPL.summary)RECORD_IMPL.summary(grid,column,options,row,element,text);else{add(text);grid.loadRecordPanel()}}else if(raw==null&&object&&cell.text==="")add("");else if(raw==null&&(!object||cell.text===undefined)||text==="")add("—","p-missing");else if(options.kind==="link"){const href=gridPresenterLink(cellValue(options.hrefColumn?row.cells[options.hrefColumn]:cell));const link=add(text,"p-link",href?"a":"span");if(href){link.href=href;if(options.newWindow){link.target="_blank";link.rel="noopener noreferrer"}}}else if(options.kind==="icon"){add(options.glyph,"cx-presenter-icon").setAttribute("aria-hidden","true");add(text)}
4
+ element.appendChild(item);return item};const more=(value,label=grid.labels.gridShowMore)=>{const button=add(label,"cx-link cx-presenter-expand","button");button.type="button";button.setAttribute("aria-haspopup","dialog");button.setAttribute("aria-label",fill(grid.labels.gridExpandCell,{column:column.title}));button.addEventListener("click",()=>grid.showReportingText(column,value,button,row.id,options.mono));return button};if(GRID_HISTORY_KINDS.includes(options.kind)){if(GRID_HISTORY_IMPL.render)GRID_HISTORY_IMPL.render(grid,column,options,cell,element,text);else grid.loadHistory()}else if(options.kind==="structured"){if(RECORD_IMPL.summary)RECORD_IMPL.summary(grid,column,options,row,element,text);else{add(text);grid.loadRecordPanel()}}else if(options.kind==="change"&&options.beforeAfter&&object&&cell.changes?.length){const change=cell.changes[0],wrap=h("span","cx-before-after"),before=h("del"),arrow=h("span"),after=h("ins");const kind={...column,kind:change.kind||column.kind};setText(before,changeCellText(change.before,kind,grid.fmt));setText(arrow,"→");arrow.setAttribute("aria-hidden","true");setText(after,changeCellText(change.after,kind,grid.fmt));if(grid.view.compare&&cellValue(row.cells[grid.view.compare.statusColumn])==="removed")wrap.append(before);else wrap.append(before,arrow,after);element.append(wrap)}else if(raw==null&&object&&cell.text==="")add("");else if(raw==null&&(!object||cell.text===undefined)||text==="")add("—","p-missing");else if(options.kind==="link"){const href=gridPresenterLink(cellValue(options.hrefColumn?row.cells[options.hrefColumn]:cell));const link=add(text,"p-link",href?"a":"span");if(href){link.href=href;if(options.newWindow){link.target="_blank";link.rel="noopener noreferrer"}}}else if(options.kind==="icon"){add(options.glyph,"cx-presenter-icon").setAttribute("aria-hidden","true");add(text)}
5
5
  else if(options.kind==="boolean"&&typeof raw==="boolean"){const value=add(raw?"":"—",raw?"p-bool-yes"+(options.inverted?" cx-bool-bad":""):"p-bool-no");value.setAttribute("role","img");value.setAttribute("aria-label",raw?grid.labels.yes:grid.labels.no);if(raw)value.appendChild(gridIcon("check"))}else if(options.kind==="state"||options.kind==="enum"){if(options.quietTones?.includes(tone))add(text,(options.quietStyle==="plain"?"cx-presenter-text":"p-muted")+(options.kind==="enum"?" p-inset":""));else if(options.kind==="state"){const state=add("","p-state"+(tone==="success"?" quiet":""));const dot=h("span","p-dot "+(tone==="success"?"quiet":tone));toneColors(dot,toneOf(tone));state.append(dot,document.createTextNode(text))}else{const pill=add(text,"p-pill "+tone);applyTone(pill,pill.className,toneOf(tone))}}else if(options.kind==="change"){const delta=cellValue(row.cells[options.deltaColumn]);if(typeof delta==="number")add((delta===0?"":delta>0?"↑ ":"↓ ")+numberFormat(grid.lang).format(Math.abs(delta)),"p-delta "+(delta===0?"flat":(delta>0)===!!options.higherIsGood?"good":"bad"));add(text)}else if(options.kind==="progress"&&typeof raw==="number"){const box=add("","p-progress"),track=h("span","track"),bar=h("span","fill "+tone),maximum=options.maximum||100;track.setAttribute("role","progressbar");track.setAttribute("aria-label",column.title);track.setAttribute("aria-valuemin","0");track.setAttribute("aria-valuemax",String(maximum));track.setAttribute("aria-valuenow",String(Math.min(maximum,Math.max(0,raw))));track.setAttribute("aria-valuetext",text);bar.style.width=Math.min(100,Math.max(0,raw/maximum*100))+"%";toneColors(bar,toneOf(tone));track.appendChild(bar);box.append(track,document.createTextNode(text));
6
6
  }else if(options.kind==="identifier"){add(text,options.mono?"p-mono":"cx-presenter-text");const secondary=options.secondaryColumn?cellText(row.cells[options.secondaryColumn],grid.columns.find(item=>item.id===options.secondaryColumn),grid.fmt):object&&cell.secondary;if(secondary)add(secondary,"p-sub")}else if(options.kind==="tags"||options.kind==="list"){const values=text.split(options.separator).map(item=>item.trim()).filter(Boolean),limit=options.kind==="tags"?3:options.limit||3;const preview=add("","cx-tag-list");element.classList.add("cx-list-cell");for(const value of values.slice(0,limit)){const tag=h("span","p-tag");setText(tag,value);preview.appendChild(tag)}
7
7
  if(values.length>limit||options.kind==="list")more(text,values.length>limit?"+"+(values.length-limit):grid.labels.gridShowMore)}else if(options.kind==="longText"){if(column.wrap){add(text,"cx-wrapped-text"+(options.mono?" p-mono":""))}else{const budget=Math.max(256,Math.ceil((column.renderWidth||column.width)/7*5));