@evotecit/canopyx 0.3.16 → 0.3.20

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 (150) 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 +74 -80
  8. package/CanopyX.Html/Assets/canopyx-calendar.js +26 -31
  9. package/CanopyX.Html/Assets/canopyx-cell-range.css +4 -4
  10. package/CanopyX.Html/Assets/canopyx-cell-range.js +16 -14
  11. package/CanopyX.Html/Assets/canopyx-changes.css +61 -79
  12. package/CanopyX.Html/Assets/canopyx-changes.js +18 -15
  13. package/CanopyX.Html/Assets/canopyx-column-groups.css +5 -1
  14. package/CanopyX.Html/Assets/canopyx-column-groups.js +15 -9
  15. package/CanopyX.Html/Assets/canopyx-compare.css +37 -0
  16. package/CanopyX.Html/Assets/canopyx-compare.js +12 -0
  17. package/CanopyX.Html/Assets/canopyx-data.js +3 -3
  18. package/CanopyX.Html/Assets/canopyx-detail-rows.css +4 -4
  19. package/CanopyX.Html/Assets/canopyx-detail-rows.js +1 -1
  20. package/CanopyX.Html/Assets/canopyx-editing.css +5 -5
  21. package/CanopyX.Html/Assets/canopyx-editing.js +2 -2
  22. package/CanopyX.Html/Assets/canopyx-enhance.js +5 -5
  23. package/CanopyX.Html/Assets/canopyx-export.js +54 -13
  24. package/CanopyX.Html/Assets/canopyx-facets.css +32 -22
  25. package/CanopyX.Html/Assets/canopyx-facets.js +19 -15
  26. package/CanopyX.Html/Assets/canopyx-filter-editor.css +75 -89
  27. package/CanopyX.Html/Assets/canopyx-filter-editor.js +20 -16
  28. package/CanopyX.Html/Assets/canopyx-flow-checks.css +3 -3
  29. package/CanopyX.Html/Assets/canopyx-flow-checks.js +1 -1
  30. package/CanopyX.Html/Assets/canopyx-flow-command.css +3 -3
  31. package/CanopyX.Html/Assets/canopyx-flow-command.js +1 -1
  32. package/CanopyX.Html/Assets/canopyx-flow-dialog.css +4 -4
  33. package/CanopyX.Html/Assets/canopyx-flow-dialog.js +1 -1
  34. package/CanopyX.Html/Assets/canopyx-flow-drafts.css +2 -2
  35. package/CanopyX.Html/Assets/canopyx-flow-drafts.js +1 -1
  36. package/CanopyX.Html/Assets/canopyx-flow-entry.css +142 -17
  37. package/CanopyX.Html/Assets/canopyx-flow-entry.js +5 -5
  38. package/CanopyX.Html/Assets/canopyx-flow-host.css +2 -2
  39. package/CanopyX.Html/Assets/canopyx-flow-host.js +1 -1
  40. package/CanopyX.Html/Assets/canopyx-flow-module.mjs +5 -5
  41. package/CanopyX.Html/Assets/canopyx-flow-pick.css +4 -4
  42. package/CanopyX.Html/Assets/canopyx-flow-pick.js +1 -1
  43. package/CanopyX.Html/Assets/canopyx-flow-preview.css +1 -1
  44. package/CanopyX.Html/Assets/canopyx-flow-preview.js +1 -1
  45. package/CanopyX.Html/Assets/canopyx-flow-records.css +2 -2
  46. package/CanopyX.Html/Assets/canopyx-flow-records.js +1 -1
  47. package/CanopyX.Html/Assets/canopyx-flow-tasks.css +4 -4
  48. package/CanopyX.Html/Assets/canopyx-flow-tasks.js +1 -1
  49. package/CanopyX.Html/Assets/canopyx-flow.css +41 -41
  50. package/CanopyX.Html/Assets/canopyx-flow.js +1 -1
  51. package/CanopyX.Html/Assets/canopyx-format-editor.css +9 -9
  52. package/CanopyX.Html/Assets/canopyx-format-editor.js +2 -2
  53. package/CanopyX.Html/Assets/canopyx-formatting.css +13 -13
  54. package/CanopyX.Html/Assets/canopyx-formatting.js +12 -11
  55. package/CanopyX.Html/Assets/canopyx-forms.css +3 -3
  56. package/CanopyX.Html/Assets/canopyx-forms.js +1 -1
  57. package/CanopyX.Html/Assets/canopyx-grid-module.mjs +220 -155
  58. package/CanopyX.Html/Assets/canopyx-grid-options.css +8 -8
  59. package/CanopyX.Html/Assets/canopyx-grid-options.js +9 -2
  60. package/CanopyX.Html/Assets/canopyx-grid.css +285 -150
  61. package/CanopyX.Html/Assets/canopyx-grid.js +220 -155
  62. package/CanopyX.Html/Assets/canopyx-grouping.css +23 -16
  63. package/CanopyX.Html/Assets/canopyx-grouping.js +24 -17
  64. package/CanopyX.Html/Assets/canopyx-highlight.css +10 -10
  65. package/CanopyX.Html/Assets/canopyx-highlight.js +1 -1
  66. package/CanopyX.Html/Assets/canopyx-interactive.css +215 -91
  67. package/CanopyX.Html/Assets/canopyx-interactive.js +47 -52
  68. package/CanopyX.Html/Assets/canopyx-labels-de.js +177 -9
  69. package/CanopyX.Html/Assets/canopyx-labels-es.js +177 -9
  70. package/CanopyX.Html/Assets/canopyx-labels-fr.js +176 -8
  71. package/CanopyX.Html/Assets/canopyx-labels-it.js +176 -8
  72. package/CanopyX.Html/Assets/canopyx-labels-pl.js +176 -8
  73. package/CanopyX.Html/Assets/canopyx-layouts.css +49 -21
  74. package/CanopyX.Html/Assets/canopyx-layouts.js +36 -14
  75. package/CanopyX.Html/Assets/canopyx-link.css +3 -3
  76. package/CanopyX.Html/Assets/canopyx-link.js +4 -4
  77. package/CanopyX.Html/Assets/canopyx-links.js +2 -2
  78. package/CanopyX.Html/Assets/canopyx-live.css +3 -5
  79. package/CanopyX.Html/Assets/canopyx-live.js +3 -3
  80. package/CanopyX.Html/Assets/canopyx-matrix.css +32 -39
  81. package/CanopyX.Html/Assets/canopyx-matrix.js +11 -11
  82. package/CanopyX.Html/Assets/canopyx-module.mjs +47 -52
  83. package/CanopyX.Html/Assets/canopyx-office-export.js +2936 -0
  84. package/CanopyX.Html/Assets/canopyx-pager.css +1 -1
  85. package/CanopyX.Html/Assets/canopyx-pager.js +1 -1
  86. package/CanopyX.Html/Assets/canopyx-path.css +45 -0
  87. package/CanopyX.Html/Assets/canopyx-path.js +28 -0
  88. package/CanopyX.Html/Assets/canopyx-pivot.css +75 -0
  89. package/CanopyX.Html/Assets/canopyx-pivot.js +72 -0
  90. package/CanopyX.Html/Assets/canopyx-plan.css +63 -0
  91. package/CanopyX.Html/Assets/canopyx-plan.js +71 -0
  92. package/CanopyX.Html/Assets/canopyx-presenters.js +2 -2
  93. package/CanopyX.Html/Assets/canopyx-record-panel.css +51 -45
  94. package/CanopyX.Html/Assets/canopyx-record-panel.js +30 -22
  95. package/CanopyX.Html/Assets/canopyx-reporting-module.mjs +262 -197
  96. package/CanopyX.Html/Assets/canopyx-reporting.css +300 -164
  97. package/CanopyX.Html/Assets/canopyx-reporting.js +262 -197
  98. package/CanopyX.Html/Assets/canopyx-stack.css +35 -0
  99. package/CanopyX.Html/Assets/canopyx-stack.js +24 -0
  100. package/CanopyX.Html/Assets/canopyx-time-slider.css +18 -0
  101. package/CanopyX.Html/Assets/canopyx-time-slider.js +27 -0
  102. package/CanopyX.Html/Assets/canopyx-timeline.css +19 -19
  103. package/CanopyX.Html/Assets/canopyx-timeline.js +6 -5
  104. package/CanopyX.Html/Assets/canopyx-tree-tools.css +47 -46
  105. package/CanopyX.Html/Assets/canopyx-tree-tools.js +15 -12
  106. package/CanopyX.Html/Assets/canopyx-ui-menu.css +41 -0
  107. package/CanopyX.Html/Assets/canopyx-ui-menu.js +64 -0
  108. package/CanopyX.Html/Assets/canopyx-ui-navigation.css +73 -0
  109. package/CanopyX.Html/Assets/canopyx-ui-navigation.js +39 -0
  110. package/CanopyX.Html/Assets/canopyx-ui-overlays.css +79 -0
  111. package/CanopyX.Html/Assets/canopyx-ui-overlays.js +45 -0
  112. package/CanopyX.Html/Assets/canopyx-ui-picker.css +12 -0
  113. package/CanopyX.Html/Assets/canopyx-ui-picker.js +6 -0
  114. package/CanopyX.Html/Assets/canopyx-ui-stepper.css +174 -0
  115. package/CanopyX.Html/Assets/canopyx-ui-stepper.js +48 -0
  116. package/CanopyX.Html/Assets/canopyx-ui-tabs.css +153 -0
  117. package/CanopyX.Html/Assets/canopyx-ui-tabs.js +47 -0
  118. package/CanopyX.Html/Assets/canopyx-values.css +5 -0
  119. package/CanopyX.Html/Assets/canopyx-values.js +13 -0
  120. package/CanopyX.Html/Assets/canopyx-view-intents.css +12 -0
  121. package/CanopyX.Html/Assets/canopyx-view-intents.js +56 -0
  122. package/CanopyX.Html/Assets/canopyx.css +212 -85
  123. package/CanopyX.Html/Assets/canopyx.js +38 -26
  124. package/JavaScript/board.d.ts +30 -0
  125. package/JavaScript/changes.d.ts +4 -1
  126. package/JavaScript/column-groups.d.ts +24 -4
  127. package/JavaScript/facets.d.ts +5 -3
  128. package/JavaScript/grid.d.ts +161 -17
  129. package/JavaScript/hosts.d.ts +16 -1
  130. package/JavaScript/index.d.ts +4 -2
  131. package/JavaScript/locales/de.mjs +177 -9
  132. package/JavaScript/locales/es.mjs +177 -9
  133. package/JavaScript/locales/fr.mjs +176 -8
  134. package/JavaScript/locales/it.mjs +176 -8
  135. package/JavaScript/locales/pl.mjs +176 -8
  136. package/JavaScript/matrix.d.ts +4 -1
  137. package/JavaScript/path.d.ts +33 -0
  138. package/JavaScript/pivot.d.ts +135 -0
  139. package/JavaScript/plan.d.ts +23 -0
  140. package/JavaScript/protocol.d.ts +18 -3
  141. package/JavaScript/records.d.ts +4 -0
  142. package/JavaScript/stack.d.ts +28 -0
  143. package/JavaScript/time-slider.d.ts +58 -0
  144. package/JavaScript/ui.d.ts +261 -0
  145. package/JavaScript/view-intents.d.ts +45 -0
  146. package/README.md +76 -187
  147. package/README.nuget.md +63 -87
  148. package/ThirdParty/OfficeIMO/LICENSE +21 -0
  149. package/ThirdParty/OfficeIMO/README.md +13 -0
  150. package/package.json +76 -3
@@ -1,8 +1,8 @@
1
- (function(global){"use strict";const installed=new WeakSet();const part={version:"cd6d93e033719439",install(context){if(installed.has(context.DataGrid.prototype))return;const{DataGrid,h,setText,fill,columnWidth,foldText,gridButton,gridIcon,gridDecorateButton,cellText,cellValue,isCellObject,numberFormat,gridFreeze,requireIdentifier,loadGridFilterEditor,CELL_CONTROLS,reachable}=context;const CG_DEPTH=3,CG_PINS=["left","none","right"];const cgBad=problem=>{throw new Error("CanopyX: column groups "+problem+".")};function cgRead(view){const declared=new Set(view.columns.map(column=>column.id)),owner=new Map(),byId=new Map();if(!Array.isArray(view.columnGroups)||view.columnGroups.length>1000)cgBad("must be an array of at most 1000 groups");const read=(input,parent,depth)=>{if(!input||typeof input!=="object"||Array.isArray(input))cgBad("must be objects");requireIdentifier(input.id,"column group ID");const columns=input.columns??[],groups=input.groups??[];if(byId.has(input.id)||typeof input.title!=="string"||!input.title.trim()||input.title.length>256||depth>CG_DEPTH||
1
+ (function(global){"use strict";const installed=new WeakSet();const part={version:"91abd99e8ff95b58",install(context){if(installed.has(context.DataGrid.prototype))return;const{DataGrid,h,setText,fill,columnWidth,foldText,gridButton,gridIcon,gridDecorateButton,cellText,cellValue,isCellObject,numberFormat,gridFreeze,requireIdentifier,loadGridFilterEditor,CELL_CONTROLS,reachable}=context;const CG_DEPTH=3,CG_PINS=["left","none","right"];const cgBad=problem=>{throw new Error("CanopyX: column groups "+problem+".")};function cgRead(view){const declared=new Set(view.columns.map(column=>column.id)),owner=new Map(),byId=new Map(),definitions=view.columnBands??view.columnGroups;if(!Array.isArray(definitions)||definitions.length>1000)cgBad("must be an array of at most 1000 groups");const read=(input,parent,depth)=>{if(!input||typeof input!=="object"||Array.isArray(input))cgBad("must be objects");requireIdentifier(input.id,"column group ID");const columns=input.columns??[],groups=input.groups??[];if(byId.has(input.id)||typeof input.title!=="string"||!input.title.trim()||input.title.length>256||depth>CG_DEPTH||
2
2
  !Array.isArray(columns)||!Array.isArray(groups)||!columns.length&&!groups.length||
3
- input.pinned!==undefined&&(parent||![null,"left","right"].includes(input.pinned)))
3
+ input.pinned!==undefined&&(view.columnBands!==undefined||parent||![null,"left","right"].includes(input.pinned)))
4
4
  cgBad("need unique IDs, titles, members, at most "+CG_DEPTH+" levels, and a pin only on a top-level group");const node={id:input.id,title:input.title,pinned:input.pinned,parent,depth,columns:[],groups:[],leaves:[]};byId.set(node.id,node);for(const id of columns){if(!declared.has(id)||owner.has(id))cgBad("name declared columns, each in one group at most");owner.set(id,node);node.columns.push(id)}
5
- for(const group of groups)node.groups.push(read(group,node,depth+1));return node};const roots=view.columnGroups.map(group=>read(group,null,1));const gather=node=>node.leaves=[...node.columns,...node.groups.flatMap(gather)];roots.forEach(gather);return{roots,byId,owner,depth:Math.max(0,...[...byId.values()].map(node=>node.depth))}}
5
+ for(const group of groups)node.groups.push(read(group,node,depth+1));return node};const roots=definitions.map(group=>read(group,null,1));const gather=node=>node.leaves=[...node.columns,...node.groups.flatMap(gather)];roots.forEach(gather);return{roots,byId,owner,depth:Math.max(0,...[...byId.values()].map(node=>node.depth))}}
6
6
  function cgPath(model,id){const path=[];for(let node=model.owner.get(id);node;node=node.parent)path.unshift(node);return path}
7
7
  function cgOrder(grid){const model=grid.columnGroupModel,rank=new Map(grid.columns.map((column,index)=>[column.id,index]));const byId=new Map(grid.columns.map(column=>[column.id,column]));const middle=ids=>ids.map(id=>rank.get(id)).sort((a,b)=>a-b)[(ids.length-1)>>1];const place=node=>[...node.columns.map(id=>({at:rank.get(id),ids:[id]})),...node.groups.map(group=>({at:middle(group.leaves),ids:place(group)}))]
8
8
  .sort((a,b)=>a.at-b.at).flatMap(item=>item.ids);const items=model.roots.map(root=>{const members=root.leaves.map(id=>byId.get(id)).sort((a,b)=>rank.get(a.id)- rank.get(b.id)),counts=new Map();for(const column of members)counts.set(column.pin||"none",(counts.get(column.pin||"none")||0)+ 1);let pin=members[0].pin||"none";for(const[side,count]of counts)if(count>counts.get(pin))pin=side;for(const column of members)column.pin=pin;return{at:middle(root.leaves),ids:place(root),pin}});for(const column of grid.columns)if(!model.owner.has(column.id))items.push({at:rank.get(column.id),ids:[column.id],pin:column.pin||"none"});items.sort((a,b)=>CG_PINS.indexOf(a.pin)- CG_PINS.indexOf(b.pin)||a.at-b.at);const order=items.flatMap(item=>item.ids);if(order.some((id,index)=>grid.columns[index].id!==id))grid.columns=order.map(id=>byId.get(id))}
@@ -13,10 +13,12 @@ const order=grid.columns.map(column=>column.id);return order.indexOf(cgFirst(gri
13
13
  function cgFirst(grid,id){const node=grid.columnGroupModel.byId.get(id);return node?grid.columns.find(column=>node.leaves.includes(column.id)).id:id}
14
14
  function cgTop(grid,node,first){const members=[...node.columns,...node.groups.map(group=>group.id)],order=grid.columns.map(column=>column.id);const at=id=>order.indexOf(cgFirst(grid,id));return members.sort((a,b)=>first?at(a)- at(b):at(b)- at(a))[0]}
15
15
  function cgMoved(grid,id,targetId,after){const model=grid.columnGroupModel,block=model.byId.get(id)?.leaves||[id],targetBlock=model.byId.get(targetId)?.leaves||[targetId];const order=grid.columns.map(column=>column.id).filter(item=>!block.includes(item));const positions=targetBlock.map(item=>order.indexOf(item)).filter(index=>index>=0);if(!positions.length)return null;order.splice(after?Math.max(...positions)+ 1:Math.min(...positions),0,...grid.columns.map(column=>column.id).filter(item=>block.includes(item)));return order}
16
- function cgHeader(grid){const model=grid.columnGroupModel,cols=grid.cols,paths=cols.map(column=>cgPath(model,column.id));const depth=Math.max(0,...paths.map(path=>path.length));for(const row of grid.groupRows||[])row.remove();grid.groupRows=[];grid.groupCells=[];for(let level=0;level<depth;level++){const row=h("div","cx-hrow cx-hgroup-row");row.setAttribute("role","row");row.setAttribute("aria-rowindex",String(level+1));for(let first=0;first<cols.length;){const node=paths[first][level];let end=first+1;if(node)while(end<cols.length&&paths[end][level]===node)end++;const cell=node?cgGroupCell(grid,node,first,end-first):h("div","cx-hcell cx-hgroup-pad");if(!node)cell.setAttribute("aria-hidden","true");cell._cxSpan={first,last:end-1};row.appendChild(cell);grid.groupCells.push(cell);first=end}
16
+ function cgHeader(grid){const model=grid.columnGroupModel,cols=grid.cols,paths=cols.map(column=>cgPath(model,column.id));const depth=Math.max(0,...paths.map(path=>path.length));for(const row of grid.groupRows||[])row.remove();grid.groupRows=[];grid.groupCells=[];for(let level=0;level<depth;level++){const row=h("div","cx-hrow cx-hgroup-row"),seen=new Set();row.setAttribute("role","row");row.setAttribute("aria-rowindex",String(level+1));for(let first=0;first<cols.length;){const node=paths[first][level];let end=first+1;if(node)while(end<cols.length&&paths[end][level]===node&&cols[end].effectivePin===cols[first].effectivePin)end++;const cell=node?cgGroupCell(grid,node,first,end-first):h("div","cx-hcell cx-hgroup-pad");if(node&&grid.view.columnBands!==undefined){cell.classList.add("cx-hband");cell.classList.toggle("cx-hband-continuation",seen.has(node));seen.add(node)}
17
+ if(!node)cell.setAttribute("aria-hidden","true");cell._cxSpan={first,last:end-1};row.appendChild(cell);grid.groupCells.push(cell);first=end}
17
18
  grid.groupRows.push(row)}
18
- grid.head.prepend(...grid.groupRows);grid.headRow.setAttribute("aria-rowindex",String(depth+1));grid.host.classList.toggle("cx-column-groups",depth>0);cgDepth(grid,depth);cgLayout(grid)}
19
- function cgGroupCell(grid,node,first,span){const cell=h("div","cx-hcell cx-hgroup"),title=h("span","cx-hgroup-title");cell.setAttribute("role","columnheader");cell.setAttribute("aria-colindex",String(first+1));cell.setAttribute("aria-colspan",String(span));cell.setAttribute("aria-label",node.title);cell.dataset.cxGroup=node.id;cell.dataset.cxLevel=String(node.depth);setText(title,node.title);title.title=node.title;cell.appendChild(title);if(grid.view.reporting?.columnReorder)cgMoveButton(grid,node,cell);cell.appendChild(cgGrip(grid,node));return cell}
19
+ grid.head.prepend(...grid.groupRows);grid.headRow.setAttribute("aria-rowindex",String(depth+1));grid.host.classList.toggle("cx-column-groups",depth>0);cgDepth(grid,depth);cgLayout(grid);grid.columnBandKey=cgBandKey(grid)}
20
+ function cgGroupCell(grid,node,first,span){const cell=h("div","cx-hcell cx-hgroup"),title=h("span","cx-hgroup-title");cell.setAttribute("role","columnheader");cell.setAttribute("aria-colindex",String(first+1));cell.setAttribute("aria-colspan",String(span));cell.setAttribute("aria-label",node.title);cell.dataset.cxGroup=node.id;cell.dataset.cxLevel=String(node.depth);setText(title,node.title);title.title=node.title;cell.appendChild(title);if(grid.view.columnBands===undefined){if(grid.view.reporting?.columnReorder)cgMoveButton(grid,node,cell);cell.appendChild(cgGrip(grid,node))}
21
+ return cell}
20
22
  function cgDepth(grid,depth){const previous=grid.headDepth||0;if(previous===depth)return;grid.headDepth=depth;const count=grid.grid.getAttribute("aria-rowcount");if(count&&count!=="-1")grid.grid.setAttribute("aria-rowcount",String(Number(count)- previous+depth));for(const row of[...grid.rendered.values(),...grid.pool])row._index=undefined;grid.scheduleRender()}
21
23
  function cgLayout(grid){const cells=grid.headCells||[],widths=cells.map(cell=>parseFloat(cell.style.width)||0);for(const row of grid.groupRows||[])row.style.width=grid.headRow.style.width;for(const cell of grid.groupCells||[]){const{first,last}=cell._cxSpan;if(last>=cells.length)continue;let width=0;for(let index=first;index<=last;index++)width +=widths[index];const head=cells[first],tail=cells[last],pin=head.dataset.cxPin;cell.style.width=width+"px";if(pin)cell.dataset.cxPin=pin;else cell.removeAttribute("data-cx-pin");cell.style.left=pin==="left"?head.style.left:"";cell.style.right=pin==="right"?tail.style.right:"";cell.classList.toggle("cx-pin-edge",cells.slice(first,last+1).some(item=>item.classList.contains("cx-pin-edge")));const grip=cell.querySelector(":scope > .cx-resize"),columns=grid.cols.slice(first,last+1);if(grip){grip.setAttribute("aria-valuemin",String(columns.reduce((sum,column)=>sum+column.minW,0)));grip.setAttribute("aria-valuemax",String(Math.min(2147483647,columns.reduce((sum,column)=>sum+column.maxW,0))));grip.setAttribute("aria-valuenow",String(Math.round(width)));grip.setAttribute("aria-valuetext",fill(grid.labels.columnWidth,{width:numberFormat(grid.lang).format(Math.round(width))}))}}
22
24
  grid.host.style.setProperty("--cx-pin-left-w",(grid.pinnedLeftWidth||0)+ "px")}
@@ -43,9 +45,13 @@ function cgChoose(grid,id,targetId,after){const node=grid.columnGroupModel.byId.
43
45
  const to=cgSibling(grid,id,targetId,after),order=to.id!==id&&cgMoved(grid,id,to.id,to.after);if(!order)return;if(!grid.columnGroupModel.owner.has(id))grid.columns.find(column=>column.id===id).pin=grid.columns.find(column=>column.id===cgFirst(grid,to.id)).pin;grid.setColumnOrder(order)}
44
46
  function cgDragChoice(grid,list,row,handle,id,event){if(event.button)return;event.preventDefault();handle.focus();handle.setPointerCapture(event.pointerId);let target=null;const clear=()=>{for(const item of list.querySelectorAll(".cx-drop-before,.cx-drop-after"))item.classList.remove("cx-drop-before","cx-drop-after")};handle.onpointermove=move=>{clear();target=null;const over=document.elementFromPoint(move.clientX,move.clientY)?.closest(".cx-grid-column-choice");if(!over||over===row||!list.contains(over))return;const box=over.getBoundingClientRect(),to=cgSibling(grid,id,over.dataset.cxColumn||over.dataset.cxGroup,move.clientY>box.top+box.height / 2);
45
47
  if(to.id===id)return;target=to;const marked=list.querySelector('[data-cx-column="'+CSS.escape(to.id)+ '"],[data-cx-group="'+CSS.escape(to.id)+ '"]');marked?.classList.add(to.after?"cx-drop-after":"cx-drop-before")};handle.onpointerup=handle.onpointercancel=up=>{handle.onpointermove=handle.onpointerup=handle.onpointercancel=null;clear();if(up.type==="pointerup"&&target)cgChoose(grid,id,target.id,target.after)}}
48
+ const cgBandKey=grid=>JSON.stringify(grid.cols.map(column=>[column.id,column.effectivePin||"none"]));function cgInstallPresentation(grid){const shared=Object.getPrototypeOf(grid);grid.buildHeader=function(...args){this.groupCells=[];this.columnBandsBuilding=true;try{shared.buildHeader.apply(this,args)}
49
+ finally{this.columnBandsBuilding=false}
50
+ cgHeader(this)};grid.applyWidths=function(...args){shared.applyWidths.apply(this,args);if(this.columnBandsBuilding||!this.cols||this.headCells?.length!==this.cols.length)return;if(this.columnBandKey!==cgBandKey(this))cgHeader(this);else cgLayout(this)};grid.lifetime.signal.addEventListener("abort",()=>{grid.host.classList.remove("cx-column-groups");grid.host.style.removeProperty("--cx-pin-left-w")},{once:true});grid.buildHeader();grid.render()}
46
51
  function cgColumn(grid,to){const node=grid.columnGroupModel.byId.get(to.id);if(!node)return grid.columns.find(column=>column.id===to.id);const members=grid.columns.filter(column=>node.leaves.includes(column.id));return to.after?members[members.length-1]:members[0]}
47
52
  function cgInstall(grid){const shared=Object.getPrototypeOf(grid);grid.buildHeader=function(){this.groupCells=[];cgOrder(this);shared.buildHeader.call(this);cgHeader(this)};grid.applyWidths=function(...args){shared.applyWidths.apply(this,args);if(this.cols&&this.headCells?.length===this.cols.length)cgUnsplit(this);cgLayout(this)};grid.setColumnPinned=function(id,pin){const root=cgPath(this.columnGroupModel,id)[0];if(root&&CG_PINS.includes(pin))for(const column of this.columns)if(root.leaves.includes(column.id))column.pin=pin;return shared.setColumnPinned.call(this,id,pin)};grid.moveReportingColumn=function(column,target,after){const to=cgSibling(this,column.id,target.id,after),landing=cgColumn(this,to);if(to.id!==column.id&&landing&&landing!==column)shared.moveReportingColumn.call(this,column,landing,to.after)};grid.moveColumnChoice=function(column,target,after){cgChoose(this,column.id,target.id,after)};grid.syncColumnControls=function(...args){return this.columnList?cgColumnControls(this):shared.syncColumnControls.apply(this,args)}}
48
- Object.assign(DataGrid.prototype,{startColumnGroups(){if(this.columnGroupModel||this.destroyed)return;if(!this.view.columnGroups?.length){this.columnGroupModel={roots:[],byId:new Map(),owner:new Map(),depth:0};return}
49
- const model=this.columnGroupModel=cgRead(this.view),byId=new Map(this.columns.map(column=>[column.id,column]));if(!this.initialState)for(const root of model.roots)if(root.pinned!==undefined)for(const id of root.leaves)byId.get(id).pin=root.pinned||"none";const titles=new Map();for(const node of model.byId.values())titles.set(node.title,(titles.get(node.title)||0)+ 1);for(const[id,node]of model.owner){const column=byId.get(id);column.section??=titles.get(node.title)>1?cgPath(model,id).map(group=>group.title).join(" · "):node.title}
53
+ Object.assign(DataGrid.prototype,{startColumnGroups(){if(this.columnGroupModel||this.destroyed)return;if(!(this.view.columnBands??this.view.columnGroups)?.length){this.columnGroupModel={roots:[],byId:new Map(),owner:new Map(),depth:0};return}
54
+ const model=this.columnGroupModel=cgRead(this.view),byId=new Map(this.columns.map(column=>[column.id,column]));if(this.view.columnBands!==undefined){cgInstallPresentation(this);return}
55
+ if(!this.initialState)for(const root of model.roots)if(root.pinned!==undefined)for(const id of root.leaves)byId.get(id).pin=root.pinned||"none";const titles=new Map();for(const node of model.byId.values())titles.set(node.title,(titles.get(node.title)||0)+ 1);for(const[id,node]of model.owner){const column=byId.get(id);column.section??=titles.get(node.title)>1?cgPath(model,id).map(group=>group.title).join(" · "):node.title}
50
56
  cgOrder(this);if(this.defaultState){const saved=new Map(this.defaultState.columns.map(column=>[column.id,column]));const declared={columnGroupModel:model,columns:this.defaultState.columns.map(entry=>({id:entry.id,pin:entry.pin??byId.get(entry.id).pin}))};for(const root of model.roots)if(root.pinned!==undefined)for(const column of declared.columns)if(root.leaves.includes(column.id))column.pin=root.pinned||"none";cgOrder(declared);this.defaultState.columns=declared.columns.map(column=>{const entry=saved.get(column.id);if("pin" in entry)entry.pin=column.pin||"none";return entry})}
51
- cgInstall(this);this.lifetime.signal.addEventListener("abort",()=>{this.cancelColumnDrag?.();this.host.classList.remove("cx-column-groups");this.host.style.removeProperty("--cx-pin-left-w")},{once:true});this.buildHeader();this.syncColumnControls();this.render()},getColumnGroups(){const model=this.columnGroupModel;if(!model?.roots.length)return[];const shown=new Set((this.cols||[]).map(column=>column.id)),order=this.columns.map(column=>column.id);const describe=node=>{const columns=order.filter(id=>node.leaves.includes(id));return Object.freeze({id:node.id,title:node.title,columns:Object.freeze(columns),visibleColumns:Object.freeze(columns.filter(id=>shown.has(id))),pin:this.columns.find(column=>column.id===columns[0]).pin||"none",groups:Object.freeze(cgSiblings(this,node).filter(id=>model.byId.has(id)).map(id=>describe(model.byId.get(id))))})};return Object.freeze(cgSiblings(this,null).filter(id=>model.byId.has(id)).map(id=>describe(model.byId.get(id))))},setColumnGroupVisible(id,visible){const node=this.columnGroupModel?.byId.get(id);if(!node||typeof visible!=="boolean"||this.destroyed)throw new Error("CanopyX: visibility needs a declared column group and a boolean.");const members=this.columns.filter(column=>node.leaves.includes(column.id));if(!visible&&this.columns.every(column=>column.hidden||members.includes(column)))throw new Error("CanopyX: keep at least one column visible.");for(const column of members)column.hidden=!visible;this.buildHeader();this.syncColumnControls();this.scheduleRender();this.noteViewPreferenceIntent?.();this.stateChanged()}});installed.add(context.DataGrid.prototype)}};(global.CanopyXParts||=[]).push(["column-groups",part])})(typeof window!=="undefined"?window:globalThis);
57
+ cgInstall(this);this.lifetime.signal.addEventListener("abort",()=>{this.cancelColumnDrag?.();this.host.classList.remove("cx-column-groups");this.host.style.removeProperty("--cx-pin-left-w")},{once:true});this.buildHeader();this.syncColumnControls();this.render()},getColumnGroups(){const model=this.columnGroupModel;if(!model?.roots.length)return[];const shown=new Set((this.cols||[]).map(column=>column.id)),order=this.columns.map(column=>column.id);const describe=node=>{const columns=order.filter(id=>node.leaves.includes(id));const pins=new Set(this.columns.filter(column=>columns.includes(column.id)).map(column=>column.pin||"none"));return Object.freeze({id:node.id,title:node.title,columns:Object.freeze(columns),visibleColumns:Object.freeze(columns.filter(id=>shown.has(id))),pin:pins.size>1?"mixed":[...pins][0],groups:Object.freeze(cgSiblings(this,node).filter(id=>model.byId.has(id)).map(id=>describe(model.byId.get(id))))})};return Object.freeze(cgSiblings(this,null).filter(id=>model.byId.has(id)).map(id=>describe(model.byId.get(id))))},getColumnBands(){return Object.freeze((this.groupCells||[]).filter(cell=>cell.dataset.cxGroup).map(cell=>{const{first,last}=cell._cxSpan;return Object.freeze({id:cell.dataset.cxGroup,title:cell.getAttribute("aria-label"),level:Number(cell.dataset.cxLevel),columnIndex:first+1,columnSpan:last-first+1,pin:cell.dataset.cxPin||"none",columns:Object.freeze(this.cols.slice(first,last+1).map(column=>column.id))})}))},setColumnGroupVisible(id,visible){if(this.view.columnBands!==undefined)throw new Error("CanopyX: presentation bands do not change column visibility; use setColumnVisible.");const node=this.columnGroupModel?.byId.get(id);if(!node||typeof visible!=="boolean"||this.destroyed)throw new Error("CanopyX: visibility needs a declared column group and a boolean.");const members=this.columns.filter(column=>node.leaves.includes(column.id));if(!visible&&this.columns.every(column=>column.hidden||members.includes(column)))throw new Error("CanopyX: keep at least one column visible.");for(const column of members)column.hidden=!visible;this.buildHeader();this.syncColumnControls();this.scheduleRender();this.noteViewPreferenceIntent?.();this.stateChanged()}});installed.add(context.DataGrid.prototype)}};(global.CanopyXParts||=[]).push(["column-groups",part])})(typeof window!=="undefined"?window:globalThis);
@@ -0,0 +1,37 @@
1
+ .cx-compare-summary{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:var(--hfx-space-3,0.75rem) var(--hfx-space-6,2rem);width:100%;box-sizing:border-box;padding:calc(var(--hfx-space-1,0.25rem)*3.5) var(--hfx-space-4,1rem);border-block-end:var(--hfx-border-width,1px) solid var(--hfx-color-surface-line,var(--hfx-color-border,light-dark(#e2e4e7,#272a30)));background:var(--hfx-color-surface-card,var(--hfx-color-surface,light-dark(#ffffff,#16181c)));color:var(--hfx-color-text-primary,var(--hfx-color-text,light-dark(#16181c,#f2f3f5)));font:inherit;}
2
+ .cx-compare-counts{display:flex;flex-wrap:wrap;gap:calc(var(--hfx-space-1,0.25rem)*2.5) var(--hfx-space-6,2rem);}
3
+ .cx-compare-count{display:inline-flex;align-items:baseline;gap:var(--hfx-space-1-5,6px);white-space:nowrap;}
4
+ .cx-compare-count strong{font-size:var(--hfx-type-title-size,1.25rem);font-variant-numeric:tabular-nums;}
5
+ .cx-compare-toggle{display:inline-flex;align-items:center;gap:var(--hfx-space-2,0.5rem);white-space:nowrap;cursor:pointer;}
6
+ .cx-compare-toggle input{accent-color:var(--hfx-color-accent,light-dark(#11776f,#37c9b8));width:calc(var(--hfx-space-1,0.25rem)*4.5);height:calc(var(--hfx-space-1,0.25rem)*4.5);margin:0;}
7
+ .cx-compare-toggle input: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);}
8
+ .cx-compare-status{font-size:var(--hfx-type-small-size,0.8125rem);font-weight:var(--hfx-weight-semibold,600);}
9
+ .cx-compare-added{color:var(--hfx-color-status-good-fg,light-dark(#177345,#4cc78a));}
10
+ .cx-compare-removed{color:var(--hfx-color-status-bad-fg,light-dark(#b8292a,#f47171));}
11
+ .cx-compare-changed{color:var(--hfx-color-status-warn-fg,light-dark(#8c5c05,#eeb640));}
12
+ .cx-compare-unchanged{color:var(--hfx-color-text-secondary,var(--hfx-color-text-muted,light-dark(#626770,#8f949d)));}
13
+ .cx-before-after{display:inline-flex;align-items:baseline;gap:var(--hfx-space-1-5,6px);max-width:100%;line-height:inherit;}
14
+ .cx-wrap .cx-before-after{flex-wrap:wrap;}
15
+ .cx-before-after del{color:var(--hfx-color-status-bad-fg,light-dark(#b8292a,#f47171));text-decoration-thickness:var(--hfx-border-width,1px);overflow-wrap:anywhere;}
16
+ .cx-before-after ins{color:var(--hfx-color-status-good-fg,light-dark(#177345,#4cc78a));text-decoration:none;overflow-wrap:anywhere;}
17
+ @media(max-width:480px){.cx-compare-summary{padding:var(--hfx-space-3,0.75rem);gap:calc(var(--hfx-space-1,0.25rem)*3.5);}.cx-compare-counts{gap:var(--hfx-space-2,0.5rem) var(--hfx-space-4,1rem);}.cx-compare-toggle{min-height:calc(var(--hfx-space-1,0.25rem)*7);}}
18
+ @media print{.cx-compare-toggle{display:none;}.cx-compare-summary{break-inside:avoid;}}
19
+ .cx-compare-column-counts{display:flex;flex-wrap:wrap;gap:var(--hfx-space-1,0.25rem) var(--hfx-space-4,1rem);flex-basis:100%;color:var(--hfx-color-text-secondary,light-dark(#4d525b,#b0b4bc));font-size:var(--hfx-type-caption-size,0.75rem);}
20
+ .cx-compare-column-counts > span{white-space:nowrap;}
21
+ .cx-compare-column-counts[hidden]{display:none;}
22
+ .cx-compare-view.cx-phone .cx-phone-fields{gap:var(--hfx-space-1,0.25rem) var(--hfx-space-2,0.5rem);}
23
+ .cx-compare-view.cx-phone .cx-phone-field{overflow:visible;}
24
+ .cx-compare-view.cx-phone .cx-phone-value{white-space:normal;overflow:visible;}
25
+ body .cx-host.cx-compare-drawer{height:var(--cx-ui-viewport-block);min-height:0;padding:0;border-radius:var(--hfx-radius-none,0);}
26
+ .cx-compare-drawer .cx-drawer-body{padding-block:var(--hfx-space-3,0.75rem);}
27
+ .cx-compare-drawer .cx-record-section{margin:0;}
28
+ .cx-compare-drawer .cx-drawer-body .cx-dl{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,2fr);gap:var(--hfx-space-3,0.75rem) var(--hfx-space-4,1rem);margin:0;padding:var(--hfx-space-3,0.75rem) 0;}
29
+ .cx-compare-drawer .cx-dl dt{font-size:var(--hfx-type-small-size,0.8125rem);color:var(--hfx-color-text-secondary,light-dark(#4d525b,#b0b4bc));}
30
+ .cx-compare-drawer .cx-dl dd{margin:0;}
31
+ .cx-compare-record-changes{margin:0;padding:0 0 var(--hfx-space-3,0.75rem);border-bottom:var(--hfx-border-width,1px) solid var(--hfx-color-surface-line,var(--hfx-color-border,light-dark(#e2e4e7,#272a30)));}
32
+ .cx-compare-record-changes h3,.cx-compare-drawer .cx-drawer-body .cx-record-toggle{margin:0;font-size:var(--hfx-type-small-size,0.8125rem);font-weight:var(--hfx-weight-semibold,600);letter-spacing:normal;text-transform:none;color:var(--hfx-color-text-secondary,light-dark(#4d525b,#b0b4bc));}
33
+ .cx-compare-drawer .cx-drawer-body .cx-record-toggle{padding:var(--hfx-space-3,0.75rem) 0 var(--hfx-space-1,0.25rem);justify-content:space-between;}
34
+ .cx-compare-drawer[data-phone="true"] .cx-dl{grid-template-columns:minmax(0,1fr);gap:var(--hfx-space-1,0.25rem);}
35
+ .cx-compare-drawer[data-phone="true"] .cx-dl dd{margin-block-end:var(--hfx-space-2,0.5rem);}
36
+ @media (forced-colors:active){.cx-before-after del{color:CanvasText;}.cx-before-after ins{color:CanvasText;}}
37
+ .cx-compare-removed-row .cx-before-after del{color:var(--hfx-color-text-secondary,var(--hfx-color-text-muted,light-dark(#626770,#8f949d)));}
@@ -0,0 +1,12 @@
1
+ (function(global){"use strict";const installed=new WeakSet();const part={version:"46245b96483e9c30",requires:["presenters"],install(context){if(installed.has(context.DataGrid.prototype))return;const{DataGrid,h,setText,fill,plural,cellValue,numberFormat,gridQueryStart,gridQuery}=context;const COMPARE_LABELS={compareSummary:"Dataset comparison",compareAdded:"Added",compareRemoved:"Removed",compareChanged:"Changed",compareUnchanged:"Unchanged",compareOnlyChanges:"Changed only",compareColumnChanges:"[one]{column}: {count} changed cell[other]{column}: {count} changed cells",compareChanges:"Changes"};function compareDefinition(grid){const value=grid.view.compare,columns=new Set(grid.columns.map(column=>column.id));if(!value||typeof value!=="object"||!columns.has(value.statusColumn)||
2
+ typeof value.onlyChanges!=="boolean"||!value.counts||!value.columns||
3
+ ["added","removed","changed","unchanged"].some(name=>!Number.isSafeInteger(value.counts[name])||value.counts[name]<0)||
4
+ Object.entries(value.columns).some(([id,count])=>!columns.has(id)||id===value.statusColumn||!Number.isSafeInteger(count)||count<0))
5
+ throw new Error("CanopyX: invalid dataset comparison.");return value}
6
+ function compareOnlyFilter(grid,filter){return filter.column===grid.view.compare.statusColumn&&filter.operator==="ne"&&filter.values?.length===1&&filter.values[0]==="unchanged"}
7
+ Object.assign(DataGrid.prototype,{startDatasetCompare(){if(this.compareStrip)return;this.labels=Object.freeze({...COMPARE_LABELS,...this.labels});const info=compareDefinition(this),strip=this.compareStrip=h("div","cx-compare-summary");strip.setAttribute("role","region");strip.setAttribute("aria-label",this.labels.compareSummary);const counts=h("div","cx-compare-counts"),number=numberFormat(this.lang);for(const name of["added","removed","changed"]){const item=h("span","cx-compare-count cx-compare-"+name),count=h("strong"),label=h("span");setText(count,number.format(info.counts[name]));setText(label,this.labels["compare"+name[0].toUpperCase()+ name.slice(1)]);item.append(count,label);counts.append(item)}
8
+ const toggle=h("label","cx-compare-toggle"),input=this.compareToggle=h("input"),caption=h("span");input.type="checkbox";input.setAttribute("role","switch");setText(caption,this.labels.compareOnlyChanges);input.checked=info.onlyChanges;toggle.append(input,caption);strip.append(counts,toggle);this.host.insertBefore(strip,this.grid);input.addEventListener("change",()=>{input.disabled=true;this.setOnlyChanges(input.checked).catch(error=>this.emit("error",{error})).finally(()=>{if(!this.destroyed){input.disabled=false;this.syncDatasetCompare()}})},{signal:this.lifetime.signal});this.host.addEventListener("canopyx:state",()=>this.syncDatasetCompare(),{signal:this.lifetime.signal});const status=this.columns.find(column=>column.id===info.statusColumn);if(!status.render)status.render=(row,element)=>{const state=cellValue(row.cells[status.id]),label=this.labels["compare"+String(state)[0]?.toUpperCase()+ String(state).slice(1)];element.closest(".cx-row")?.classList.toggle("cx-compare-removed-row",state==="removed");const badge=h("span","cx-compare-status cx-compare-"+state);setText(badge,label||state);element.append(badge);return true};const details=h("div","cx-compare-column-counts");for(const column of this.columns){const count=Object.hasOwn(info.columns,column.id)?info.columns[column.id]:0;if(!count)continue;const caption=fill(plural(this.labels.compareColumnChanges,count,this.lang),{column:column.title,count:number.format(count)});column.headerDescription=[column.headerDescription,caption].filter(Boolean).join(". ");const item=h("span");setText(item,caption);details.append(item)}
9
+ details.hidden=!details.firstChild;strip.append(details);this.host.classList.add("cx-compare-view");this.buildHeader();if(info.onlyChanges){const query=this.view.query||{};this.view.query={...query,filters:[...(query.filters||[]).filter(filter=>!compareOnlyFilter(this,filter)),{column:info.statusColumn,operator:"ne",values:["unchanged"]}]};this.defaultState.query=gridQuery(this.view,gridQueryStart(this.view.query))}},getOnlyChanges(){return!!this.view.compare&&!!this.getQuery()?.filters?.some(filter=>compareOnlyFilter(this,filter))},setOnlyChanges(enabled){if(typeof enabled!=="boolean"||!this.view.compare)throw new Error("CanopyX: only-changes requires a comparison and a boolean.");const query=this.queryForChange(true),filters=(query.filters||[]).filter(filter=>!compareOnlyFilter(this,filter));if(enabled)filters.push({column:this.view.compare.statusColumn,operator:"ne",values:["unchanged"]});return this.setQuery(gridQueryStart(query,{filters})).then(result=>{this.syncDatasetCompare();return result})},syncDatasetCompare(){if(this.compareToggle)this.compareToggle.checked=this.getOnlyChanges()}});const COMPARE_RECORD_BASE={phoneLineColumns:DataGrid.prototype.phoneLineColumns,filterCaption:DataGrid.prototype.filterCaption};Object.assign(DataGrid.prototype,{filterCaption(filter){return this.view.compare&&compareOnlyFilter(this,filter)?this.labels.compareOnlyChanges:COMPARE_RECORD_BASE.filterCaption?.call(this,filter)},phoneLineColumns(row){if(!this.view.compare||!row)return COMPARE_RECORD_BASE.phoneLineColumns.call(this,row);if(this.host.clientWidth>640||(this.layoutMode||"fit")!=="fit")return[];return cellValue(row.cells[this.view.compare.statusColumn])==="changed"?
10
+ this.hiddenViewColumns().filter(column=>row.cells[column.id]?.changes?.length):[]}});DataGrid.drawerHook(5,function(dialog){if(!this.view.compare)return;dialog.classList.add("cx-compare-drawer");dialog.dataset.modal="true";dialog._cxOverlay.setModal(true);let paints;dialog.addEventListener("close",()=>paints?.abort(),{once:true});dialog.addEventListener("cx-drawer-paint",()=>{paints?.abort();paints=new AbortController();const row=dialog._cxRecord,body=dialog.querySelector(".cx-drawer-body");body.querySelector(".cx-compare-record-changes")?.remove();const changed=this.columns.filter(column=>column.drawer!==false&&row.cells[column.id]?.changes?.length);const status=cellValue(row.cells[this.view.compare.statusColumn]);if(status==="removed")for(const column of changed){for(const block of body.querySelectorAll('.cx-record-block[data-cx-field="'+CSS.escape(column.id)+ '"]')){const list=h("dl","cx-dl"),term=h("dt"),value=h("dd"),old=h("del");setText(term,column.title);term.dataset.cxField=value.dataset.cxField=column.id;const earlier={...row,cells:{...row.cells,[column.id]:row.cells[column.id].changes[0].before}};this.paintDrawerValue(column,earlier,old,{signal:paints.signal});value.append(old);list.append(term,value);block.replaceWith(list)}}
11
+ if(!changed.length||status!=="changed")return;const section=h("section","cx-compare-record-changes"),heading=h("h3"),list=h("dl","cx-dl");setText(heading,this.labels.compareChanges);section.append(heading,list);section.setAttribute("aria-label",this.labels.compareChanges);for(const column of changed){const term=h("dt"),value=h("dd");setText(term,column.title);term.dataset.cxField=value.dataset.cxField=column.id;this.paintDrawerValue(column,row,value,{signal:paints.signal});list.append(term,value);for(const field of body.querySelectorAll('[data-cx-field="'+CSS.escape(column.id)+ '"]')){const block=field.closest(".cx-record-block");if(block)block.remove();else field.remove()}}
12
+ body.prepend(section)})});installed.add(context.DataGrid.prototype)}};(global.CanopyXParts||=[]).push(["compare",part])})(typeof window!=="undefined"?window:globalThis);
@@ -1,4 +1,4 @@
1
- (function(global){"use strict";const installed=new WeakSet();const part={version:"d74a01c46f1c40cc",install(context){if(installed.has(context.GRID_DATA_IMPL))return;const{GRID_DATA_IMPL,PROTOCOL,validateGridRows,inlineGridSource,loadSidecar,gridIdOrder}=context;const ROW_SCRIPTS=':scope>script[type="application/x-canopyx-rows"]';const nextTask=()=>globalThis.scheduler?.yield?scheduler.yield():new Promise(resolve=>setTimeout(resolve));async function inflateRows(text){if(typeof DecompressionStream!=="function")throw new Error("CanopyX: this browser cannot read compressed row data.");let bytes;if(Uint8Array.fromBase64)bytes=Uint8Array.fromBase64(text);else{const binary=atob(text);bytes=new Uint8Array(binary.length);for(let i=0;i<binary.length;i++)bytes[i]=binary.charCodeAt(i)}
1
+ (function(global){"use strict";const installed=new WeakSet();const part={version:"bab770170bd2cfdd",install(context){if(installed.has(context.GRID_DATA_IMPL))return;const{GRID_DATA_IMPL,PROTOCOL,validateGridRows,repairGridCell,inlineGridSource,loadSidecar,gridIdOrder,gridInlineRevision}=context;const ROW_SCRIPTS=':scope>script[type="application/x-canopyx-rows"]';const nextTask=()=>globalThis.scheduler?.yield?scheduler.yield():new Promise(resolve=>setTimeout(resolve));async function inflateRows(text){if(typeof DecompressionStream!=="function")throw new Error("CanopyX: this browser cannot read compressed row data.");let bytes;if(Uint8Array.fromBase64)bytes=Uint8Array.fromBase64(text);else{const binary=atob(text);bytes=new Uint8Array(binary.length);for(let i=0;i<binary.length;i++)bytes[i]=binary.charCodeAt(i)}
2
2
  return new Response(new Blob([bytes]).stream().pipeThrough(new DecompressionStream("gzip"))).text()}
3
3
  function rowDataSource(grid,outer){const view=grid.view,data=view.rowData,rows=view.rows,ids=new Set(),declared=new Set(view.columns.map(column=>column.id));const columns=data.columns,chunks=data.chunks,sidecar=data.base!==undefined;if(data.version!==1||!Array.isArray(columns)||!columns.length||new Set(columns).size!==columns.length||columns.some(id=>!declared.has(id))||
4
4
  ![undefined,"gzip"].includes(data.encoding)||![undefined,true,false].includes(data.ordered)||
@@ -7,9 +7,9 @@ sidecar&&typeof data.base!=="string"||
7
7
  throw new Error("CanopyX: invalid grid rowData.");const scripts=!sidecar&&!Array.isArray(chunks)?grid.host.querySelectorAll(ROW_SCRIPTS):null;const total=Array.isArray(chunks)?chunks.length:chunks;if(scripts&&scripts.length!==total)throw new Error("CanopyX: the grid has "+scripts.length+" row data scripts, not "+total+".");const files=[];rows.length=0;const read=index=>Array.isArray(chunks)?chunks[index]:sidecar?files[index]||=loadSidecar(data.base,view.id,"rows/"+index):scripts[index].textContent;const expand=row=>{if(!Array.isArray(row)||row.length>columns.length+1)throw new Error("CanopyX: invalid compact grid row.");const key=row[0],cells={};for(let c=1;c<row.length;c++){if(row[c]===undefined)throw new Error("CanopyX: compact grid cells cannot be undefined; use null for an empty cell.");if(row[c]!=null)columns[c-1]==="__proto__"?Object.defineProperty(cells,"__proto__",{value:row[c],enumerable:true,writable:true,configurable:true}):cells[columns[c-1]]=row[c]}
8
8
  if(key===null||typeof key!=="object")return{id:key,cells};const record={id:key.id,cells};if(key.tone!==undefined)record.tone=key.tone;if(key.className!==undefined)record.className=key.className;return record};let previous=null,finished=false,ordered=data.ordered===true,legacyOrder=false,advance,progress;const retirePrefix=()=>{grid.pages.clear();grid.rowCache.clear();grid.placeholders.clear();grid.focused=null;if(grid.snapshot&&!grid.pendingQuery)grid.setQuery(grid.queryForChange(true),{pageLength:grid.pageLength,allRows:grid.pageAll}).catch(()=>{});grid.version++;grid.scheduleRender()};const waitForChunk=()=>progress=new Promise(resolve=>advance=resolve);waitForChunk();const done=(async()=>{const active=()=>{if(grid.destroyed)throw new DOMException("Grid was destroyed.","AbortError")};for(let index=0;index<total;index++){active();let chunk=await read(index);active();if(sidecar&&index+1<total)read(index+1).catch(()=>{});if(typeof chunk==="string"){if(data.encoding){chunk=await inflateRows(chunk);active()}
9
9
  chunk=JSON.parse(chunk)}
10
- if(!Array.isArray(chunk))throw new Error("CanopyX: a row data chunk is not an array of rows.");const decoded=chunk.map(expand);validateGridRows(decoded,view.columns,ids);if(ordered)for(const row of decoded){if(previous!==null&&gridIdOrder(previous,row.id)>=0){if(!(previous<row.id))throw new Error("CanopyX: row data is not in its declared identity order.");ordered=false;legacyOrder=true;retirePrefix();break}
10
+ if(!Array.isArray(chunk))throw new Error("CanopyX: a row data chunk is not an array of rows.");const decoded=validateGridRows(chunk.map(expand),view.columns,ids,repairGridCell);if(ordered)for(const row of decoded){if(previous!==null&&gridIdOrder(previous,row.id)>=0){if(!(previous<row.id))throw new Error("CanopyX: row data is not in its declared identity order.");ordered=false;legacyOrder=true;retirePrefix();break}
11
11
  previous=row.id}
12
12
  for(const row of decoded)rows.push(row);if(rows.length>data.count)break;files[index]=null;advance();waitForChunk();await nextTask()}
13
- active();if(rows.length!==data.count)throw new Error("CanopyX: row data holds "+rows.length+" rows, not "+data.count+".");finished=true;ids.clear();if(legacyOrder)retirePrefix();if(Array.isArray(chunks))chunks.fill(null);scripts?.forEach(script=>{script.textContent=""});grid.version++;grid.scheduleRender()})();done.catch(()=>outer.reset());done.then(()=>{if(!grid.destroyed){grid.version++;grid.scheduleRender?.()}},()=>{});let complete=null;outer.invalidate=()=>{complete=null};const all=async()=>{await done;return complete||=inlineGridSource(view)};outer.decoded=done;if(!Object.hasOwn(grid,"columnFacetItems"))grid.columnFacetItems=async function(...args){await outer.decoded;return Object.getPrototypeOf(this).columnFacetItems.apply(this,args)};if(view.reporting?.facetColumns?.length)outer.facet=async(request,options)=>(await all()).facet(request,options);return{async query(query,options){if(ordered&&!view.reporting&&!view.footers?.length&&!query.search&&!query.filters.length&&!query.filterGroup&&!query.sort.length){const end=Math.min(data.count,query.offset+query.limit);while(rows.length<end&&!finished)await Promise.race([progress,done]);if(options.signal.aborted)throw new DOMException("Grid cancelled.","AbortError");if(!finished&&ordered)return{protocol:PROTOCOL,type:"gridPage",view:view.id,revision:"inline",offset:query.offset,total:data.count,matched:data.count,items:rows.slice(query.offset,end)}}
13
+ active();if(rows.length!==data.count)throw new Error("CanopyX: row data holds "+rows.length+" rows, not "+data.count+".");finished=true;ids.clear();if(legacyOrder)retirePrefix();if(Array.isArray(chunks))chunks.fill(null);scripts?.forEach(script=>{script.textContent=""});grid.version++;grid.scheduleRender()})();done.catch(()=>outer.reset());done.then(()=>{if(!grid.destroyed){grid.version++;grid.scheduleRender?.()}},()=>{});let complete=null;outer.invalidate=()=>{complete=null};const all=async()=>{await done;return complete||=inlineGridSource(view)};outer.decoded=done;if(!Object.hasOwn(grid,"columnFacetItems"))grid.columnFacetItems=async function(...args){await outer.decoded;return Object.getPrototypeOf(this).columnFacetItems.apply(this,args)};if(view.reporting?.facetColumns?.length)outer.facet=async(request,options)=>(await all()).facet(request,options);return{async query(query,options){if(ordered&&!view.reporting&&!view.footers?.length&&!view.columns.some(column=>column.calculation!==undefined)&&!query.search&&!query.filters.length&&!query.filterGroup&&!query.tupleFilters?.length&&!query.sort.length){const end=Math.min(data.count,query.offset+query.limit);while(rows.length<end&&!finished)await Promise.race([progress,done]);if(options.signal.aborted)throw new DOMException("Grid cancelled.","AbortError");if(!finished&&ordered)return{protocol:PROTOCOL,type:"gridPage",view:view.id,revision:gridInlineRevision(view),offset:query.offset,total:data.count,matched:data.count,items:rows.slice(query.offset,end)}}
14
14
  return(await all()).query(query,options)}}}
15
15
  GRID_DATA_IMPL.source=rowDataSource;installed.add(context.GRID_DATA_IMPL)}};(global.CanopyXParts||=[]).push(["data",part])})(typeof window!=="undefined"?window:globalThis);
@@ -1,17 +1,17 @@
1
1
  /* ---------- detail rows: the expander,the row and the content inside the visible width ---------- */
2
- .cx-detail-toggle{flex:none;display:inline-grid;place-items:center;width:var(--cx-size-20px);height:var(--cx-size-20px);margin-inline-end:var(--cx-size-4px);padding:0;border:0;border-radius:var(--hfx-radius-sm,4px);background:transparent;color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));cursor:pointer;}
2
+ .cx-detail-toggle{flex:none;display:inline-grid;place-items:center;width:var(--cx-size-20px);height:var(--cx-size-20px);margin-inline-end:var(--cx-size-4px);padding:0;border:0;border-radius:var(--hfx-radius-sm,4px);background:transparent;color:var(--hfx-color-text-secondary,var(--hfx-color-text-muted,light-dark(#626770,#8f949d)));cursor:pointer;}
3
3
  .cx-detail-toggle::before{content:"";width:var(--cx-size-6px);height:var(--cx-size-6px);border-right:var(--cx-size-1d6px) solid currentColor;border-bottom:var(--cx-size-1d6px) solid currentColor;transform:translateX(var(--cx-size-neg-1px)) rotate(-45deg);transition:transform .12s;}
4
4
  .cx-detail-toggle[aria-expanded="true"]::before{transform:translateY(var(--cx-size-neg-1px)) rotate(45deg);}
5
- .cx-detail-toggle:hover{color:var(--hfx-color-text,light-dark(#16181c,#f2f3f5));background:var(--hfx-color-hover,light-dark(#f5f5f5,#232529));}
5
+ .cx-detail-toggle:hover{color:var(--hfx-color-text-primary,var(--hfx-color-text,light-dark(#16181c,#f2f3f5)));background:var(--hfx-color-hover,light-dark(#f5f5f5,#232529));}
6
6
  .cx-detail-toggle: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);}
7
7
  .cx-detail-toggle[aria-expanded="true"]{color:var(--hfx-color-accent,light-dark(#11776f,#37c9b8));}
8
8
  .cx-data-grid .cx-row.cx-detail-row{background:var(--hfx-color-row-alt,light-dark(#fafafa,#1d1f23));border-bottom:var(--cx-size-1px) solid var(--hfx-color-border-strong,light-dark(#cdd0d5,#363a42));}
9
9
  .cx-data-grid .cx-row.cx-detail-row:hover{background:var(--hfx-color-row-alt,light-dark(#fafafa,#1d1f23));}
10
10
  .cx-data-grid .cx-detail-cell{flex:none;width:100%;padding:0;border-right:0;overflow:visible;}
11
11
  .cx-data-grid .cx-detail-row .cx-detail-body{padding:var(--cx-size-10px) var(--cx-size-12px) var(--cx-size-12px);box-sizing:border-box;}
12
- .cx-detail-pending{color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));font-style:italic;}
12
+ .cx-detail-pending{color:var(--hfx-color-text-secondary,var(--hfx-color-text-muted,light-dark(#626770,#8f949d)));font-style:italic;}
13
13
  .cx-detail-error{margin:0 0 var(--cx-size-8px);color:var(--hfx-color-status-bad-fg,light-dark(#b8292a,#f47171));}
14
- .cx-detail-view{height:var(--cx-size-320px);border:var(--cx-size-1px) solid var(--hfx-color-border,light-dark(#e2e4e7,#272a30));border-radius:var(--hfx-radius-control,var(--hfx-radius-md,6px));background:var(--hfx-color-surface,light-dark(#ffffff,#16181c));}
14
+ .cx-detail-view{height:var(--cx-size-320px);border:var(--cx-size-1px) solid var(--hfx-color-surface-line,var(--hfx-color-border,light-dark(#e2e4e7,#272a30)));border-radius:var(--hfx-radius-control,var(--hfx-radius-md,6px));background:var(--hfx-color-surface-card,var(--hfx-color-surface,light-dark(#ffffff,#16181c)));}
15
15
  .cx-detail-view.cx-host{padding:0;}
16
16
  .cx-phone .cx-detail-view{height:var(--cx-size-280px);}
17
17
  @media (prefers-reduced-motion:reduce){.cx-detail-toggle::before{transition:none;}}
@@ -1,4 +1,4 @@
1
- (function(global){"use strict";const installed=new WeakSet();const part={version:"e4150bb67f40d597",install(context){if(installed.has(context.DataGrid.prototype))return;const{DataGrid,GRID_ROWS,GRID_REPORTING,h,setText,fill,gridButton,gridFreeze,cellText,formatting,labelsFor,requireIdentifier,GridRowOffsets,TreeGrid,createDataGrid,createTreeGrid,disposeRowHostCells,OWN_ARROWS,hostHtml}=context;const ROWS_BASE={};for(const name of["render","renderPass","trimInactiveCache","syncRowOffsets","setFocus","onKey","fillRow","destroy"])
1
+ (function(global){"use strict";const installed=new WeakSet();const part={version:"1892f1fd9d035c0f",install(context){if(installed.has(context.DataGrid.prototype))return;const{DataGrid,GRID_ROWS,GRID_REPORTING,h,setText,fill,gridButton,gridFreeze,cellText,formatting,labelsFor,requireIdentifier,GridRowOffsets,TreeGrid,createDataGrid,createTreeGrid,disposeRowHostCells,OWN_ARROWS,hostHtml}=context;const ROWS_BASE={};for(const name of["render","renderPass","trimInactiveCache","syncRowOffsets","setFocus","onKey","fillRow","destroy"])
2
2
  ROWS_BASE[name]=DataGrid.prototype[name];GRID_ROWS.view=ROWS_BASE;function rowsLowerBound(list,value){let low=0,high=list.length;while(low<high){const middle=(low+high)>>>1;if(list[middle]<value)low=middle+1;else high=middle}
3
3
  return low}
4
4
  function rowsAtOrBefore(list,value,field){let low=0,high=list.length;while(low<high){const middle=(low+high)>>>1;if(list[middle][field]<=value)low=middle+1;else high=middle}
@@ -7,19 +7,19 @@
7
7
  .cx-data-grid .cx-cell.cx-editing > :not(.cx-editor-host){display:none;}
8
8
  .cx-editor-host{flex:1;min-width:0;height:100%;display:flex;align-items:center;}
9
9
  .cx-editor-combo{display:flex;flex:1;min-width:0;height:100%;align-items:center;}
10
- .cx-editor{box-sizing:border-box;width:100%;min-width:0;height:calc(100% - var(--cx-size-4px));margin:0;padding:0 var(--cx-size-6px);font:inherit;color:var(--hfx-color-text,light-dark(#16181c,#f2f3f5));background:var(--hfx-color-surface,light-dark(#ffffff,#16181c));border:var(--cx-size-1px) solid var(--hfx-color-accent,light-dark(#11776f,#37c9b8));border-radius:var(--hfx-radius-sm,4px);outline:none;color-scheme:inherit;}
10
+ .cx-editor{box-sizing:border-box;width:100%;min-width:0;height:calc(100% - var(--cx-size-4px));margin:0;padding:0 var(--cx-size-6px);font:inherit;color:var(--hfx-color-text-primary,var(--hfx-color-text,light-dark(#16181c,#f2f3f5)));background:var(--hfx-color-surface-card,var(--hfx-color-surface,light-dark(#ffffff,#16181c)));border:var(--cx-size-1px) solid var(--hfx-color-accent,light-dark(#11776f,#37c9b8));border-radius:var(--hfx-radius-sm,4px);outline:none;color-scheme:inherit;}
11
11
  .cx-editor:focus-visible{box-shadow:0 0 0 var(--cx-size-2px) var(--hfx-color-accent-subtle,light-dark(#e7f1f1,#1b3435));}
12
12
  .cx-editor[aria-invalid="true"]{border-color:var(--hfx-color-status-bad-fg,light-dark(#b8292a,#f47171));}
13
13
  .cx-kind-number .cx-editor{text-align:right;font-variant-numeric:tabular-nums;}
14
14
  .cx-editor.cx-editor-toggle{flex:none;width:var(--cx-size-18px);height:var(--cx-size-18px);margin:0 auto 0 var(--cx-size-6px);accent-color:var(--hfx-color-accent,light-dark(#11776f,#37c9b8));}
15
- .cx-edit-tip{position:fixed;inset:auto;margin:0;box-sizing:border-box;background:var(--hfx-color-surface-raised,light-dark(#ffffff,#1e2126));color:var(--hfx-color-text,light-dark(#16181c,#f2f3f5));border:var(--cx-size-1px) solid var(--hfx-color-border-strong,light-dark(#cdd0d5,#363a42));border-radius:var(--hfx-radius-overlay,var(--hfx-radius-lg,10px));box-shadow:var(--hfx-elevation-2,0 1px 2px rgba(22,24,28,.05),0 6px 18px -10px rgba(22,24,28,.18));font:var(--cx-size-13d5px)/1.4 var(--hfx-font-sans,"Segoe UI Variable Text","Segoe UI",system-ui,-apple-system,"Helvetica Neue",Arial,sans-serif);}
15
+ .cx-edit-tip{position:fixed;inset:auto;margin:0;box-sizing:border-box;background:var(--hfx-color-surface-raised,light-dark(#ffffff,#1e2126));color:var(--hfx-color-text-primary,var(--hfx-color-text,light-dark(#16181c,#f2f3f5)));border:var(--cx-size-1px) solid var(--hfx-color-border-strong,light-dark(#cdd0d5,#363a42));border-radius:var(--hfx-radius-overlay,var(--hfx-radius-lg,10px));box-shadow:var(--hfx-elevation-2,0 1px 2px rgba(22,24,28,.05),0 6px 18px -10px rgba(22,24,28,.18));font:var(--cx-size-13d5px)/1.4 var(--hfx-font-sans,"Segoe UI Variable Text","Segoe UI",system-ui,-apple-system,"Helvetica Neue",Arial,sans-serif);}
16
16
  .cx-edit-tip{max-width:var(--cx-size-280px);padding:var(--cx-size-6px) var(--cx-size-10px);border-left:var(--cx-size-3px) solid var(--hfx-color-status-bad-fg,light-dark(#b8292a,#f47171));white-space:normal;pointer-events:none;}
17
17
  .cx-editbar{display:flex;align-items:center;gap:var(--cx-size-4px);height:var(--hfx-control-height-md,calc(36px - var(--cx-compact,0) * 8px));padding:0 var(--cx-size-4px) 0 var(--cx-size-10px);border-radius:var(--hfx-radius-control,var(--hfx-radius-md,6px));background:var(--hfx-color-surface-sunken,light-dark(#f7f8f9,#111317));font-size:var(--cx-size-13d5px);}
18
18
  .cx-editbar[hidden]{display:none;}
19
19
  .cx-editbar-text{margin-right:var(--cx-size-4px);white-space:nowrap;}
20
20
  .cx-editbar-errors{color:var(--hfx-color-status-bad-fg,light-dark(#b8292a,#f47171));}
21
- .cx-data-grid .cx-editbar .cx-btn{height:var(--cx-size-28px);min-height:var(--cx-size-28px);font-size:var(--cx-size-13px);padding:0 var(--cx-size-9px);}
22
- .cx-data-grid .cx-editbar .icon{width:var(--cx-size-28px);padding:0;}
21
+ .cx-data-grid .cx-editbar :is(.cx-btn,.cx-button){block-size:var(--hfx-control-height-sm,calc(28px - var(--cx-compact,0) * 4px));min-block-size:var(--hfx-control-height-sm,calc(28px - var(--cx-compact,0) * 4px));padding:0 var(--hfx-space-2,0.5rem);}
22
+ .cx-data-grid .cx-editbar .icon{inline-size:var(--hfx-control-height-sm,calc(28px - var(--cx-compact,0) * 4px));padding:0;}
23
23
  .cx-host .cx-edit-save,.cx-host .cx-edit-save:hover{background:var(--hfx-color-accent,light-dark(#11776f,#37c9b8));border-color:var(--hfx-color-accent,light-dark(#11776f,#37c9b8));color:var(--hfx-color-accent-contrast,light-dark(#ffffff,#0c0d10));}
24
24
  .cx-host .cx-edit-save:disabled{opacity:.45;}
25
25
  .cx-phone .cx-editbar{order:10;flex:1 1 100%;flex-wrap:wrap;height:auto;min-height:var(--cx-size-44px);padding-block:var(--cx-size-4px);}
@@ -30,7 +30,7 @@
30
30
  .cx-drawer.cx-edit-form.cx-phone .cx-dl dd .cx-editor{height:var(--cx-size-44px);}
31
31
  .cx-drawer.cx-edit-form .cx-editor-host{height:auto;}
32
32
  .cx-edit-message{margin-top:var(--cx-size-4px);font-size:var(--cx-size-13px);color:var(--hfx-color-status-bad-fg,light-dark(#b8292a,#f47171));}
33
- .cx-drawer-f{flex:none;display:flex;align-items:center;justify-content:flex-end;gap:var(--cx-size-8px);padding:var(--cx-size-12px) var(--cx-size-16px);border-top:var(--cx-size-1px) solid var(--hfx-color-border,light-dark(#e2e4e7,#272a30));}
33
+ .cx-drawer-f{flex:none;display:flex;align-items:center;justify-content:flex-end;gap:var(--cx-size-8px);padding:var(--cx-size-12px) var(--cx-size-16px);border-top:var(--cx-size-1px) solid var(--hfx-color-surface-line,var(--hfx-color-border,light-dark(#e2e4e7,#272a30)));}
34
34
  .cx-drawer-summary{margin:0 auto 0 0;font-size:var(--cx-size-13px);color:var(--hfx-color-status-bad-fg,light-dark(#b8292a,#f47171));}
35
35
  @media (forced-colors:active){
36
36
  .cx-data-grid .cx-cell.cx-dirty{text-decoration:underline dotted;}
@@ -1,4 +1,4 @@
1
- (function(global){"use strict";const installed=new WeakSet();const part={version:"579a74b205923fd5",requires:["filter-editor","forms"],install(context){if(installed.has(context.DataGrid.prototype))return;const{DataGrid,FORMS_IMPL,h,setText,fill,gridButton,gridDecorateButton,GRID_ICONS,cellValue,cellText,isCellObject,gridFreeze,gridMomentFromParts,numberFormat,gridQuery,foldText,requireIdentifier,checkDocument,validateGridRows,inlineGridSource,PROTOCOL,boundedPattern,validateGridFacetPage,RECORD_IMPL}=context;const{EDIT_EDITORS,editOptions,formRuleSpec,formCheck,FORM_LABELS}=FORMS_IMPL;const EDIT_LABELS={...FORM_LABELS,gridEditCell:"Edit {column}",gridEditSave:"Save",gridEditCancel:"Cancel",gridEditDiscard:"Discard",gridEditUndo:"Undo",gridEditRedo:"Redo",gridEditEdited:"Edited",gridEditChanges:"Unsaved changes: {count}",gridEditErrors:"Errors: {count}",gridEditSaving:"Saving…",gridEditSaved:"Saved rows: {count}",gridEditFailed:"Rows not saved: {count}",gridEditConflict:"Someone else changed this row. Reload it and try again.",gridEditRejected:"Not saved: {error}",gridEditNotSaved:"Not saved.",gridEditUndone:"Undone: {column}",gridEditRedone:"Redone: {column}"};const editCopy=value=>gridFreeze(JSON.parse(JSON.stringify(value===undefined?null:value)));const editSame=(left,right)=>left===right||left==null&&right==null;const editScalar=value=>value===null||typeof value==="string"||typeof value==="boolean"||Number.isFinite(value);const editBad=(id,problem)=>{throw new Error("CanopyX: column "+id+" "+problem+".")};function editSpec(column,editors,columns){const id=column.id,raw=column.editor===undefined?{}:typeof column.editor==="string"?{type:column.editor}:column.editor;const rules=column.validation===undefined?{}:column.validation,object=value=>value&&typeof value==="object"&&!Array.isArray(value);if(!object(raw)||!object(rules))editBad(id,"has an invalid editor or validation");const type=raw.type??column.kind;if(typeof type!=="string"||!Object.hasOwn(editors,type))editBad(id,"names an editor nobody registered");const fits={number:["number"],text:["text"],toggle:["text"],date:["datetime","text"],datetime:["datetime","text"],time:["datetime","text"]}[type];if(fits&&!fits.includes(column.kind))editBad(id,"cannot use a "+type+" editor for its kind");const{pattern,rules:checks}=formRuleSpec(raw,rules,problem=>editBad(id,problem));const lookup=raw.lookup??(type==="lookup"?{}:null);if(lookup&&(!object(lookup)||lookup.column!==undefined&&!columns.some(item=>item.id===lookup.column)||
1
+ (function(global){"use strict";const installed=new WeakSet();const part={version:"88e4ca705a7eb0c3",requires:["filter-editor","forms"],install(context){if(installed.has(context.DataGrid.prototype))return;const{DataGrid,FORMS_IMPL,h,setText,fill,gridButton,gridDecorateButton,GRID_ICONS,cellValue,cellText,isCellObject,gridFreeze,gridMomentFromParts,numberFormat,gridQuery,foldText,requireIdentifier,checkDocument,validateGridRows,inlineGridSource,PROTOCOL,boundedPattern,validateGridFacetPage,RECORD_IMPL}=context;const{EDIT_EDITORS,editOptions,formRuleSpec,formCheck,FORM_LABELS}=FORMS_IMPL;const EDIT_LABELS={...FORM_LABELS,gridEditCell:"Edit {column}",gridEditSave:"Save",gridEditCancel:"Cancel",gridEditDiscard:"Discard",gridEditUndo:"Undo",gridEditRedo:"Redo",gridEditEdited:"Edited",gridEditChanges:"Unsaved changes: {count}",gridEditErrors:"Errors: {count}",gridEditSaving:"Saving…",gridEditSaved:"Saved rows: {count}",gridEditFailed:"Rows not saved: {count}",gridEditConflict:"Someone else changed this row. Reload it and try again.",gridEditRejected:"Not saved: {error}",gridEditNotSaved:"Not saved.",gridEditUndone:"Undone: {column}",gridEditRedone:"Redone: {column}"};const editCopy=value=>gridFreeze(JSON.parse(JSON.stringify(value===undefined?null:value)));const editSame=(left,right)=>left===right||left==null&&right==null;const editScalar=value=>value===null||typeof value==="string"||typeof value==="boolean"||Number.isFinite(value);const editBad=(id,problem)=>{throw new Error("CanopyX: column "+id+" "+problem+".")};function editSpec(column,editors,columns){const id=column.id,raw=column.editor===undefined?{}:typeof column.editor==="string"?{type:column.editor}:column.editor;const rules=column.validation===undefined?{}:column.validation,object=value=>value&&typeof value==="object"&&!Array.isArray(value);if(!object(raw)||!object(rules))editBad(id,"has an invalid editor or validation");const type=raw.type??column.kind;if(typeof type!=="string"||!Object.hasOwn(editors,type))editBad(id,"names an editor nobody registered");const fits={number:["number"],text:["text"],toggle:["text"],date:["datetime","text"],datetime:["datetime","text"],time:["datetime","text"]}[type];if(fits&&!fits.includes(column.kind))editBad(id,"cannot use a "+type+" editor for its kind");const{pattern,rules:checks}=formRuleSpec(raw,rules,problem=>editBad(id,problem));const lookup=raw.lookup??(type==="lookup"?{}:null);if(lookup&&(!object(lookup)||lookup.column!==undefined&&!columns.some(item=>item.id===lookup.column)||
2
2
  lookup.limit!==undefined&&!(Number.isInteger(lookup.limit)&&lookup.limit>=1&&lookup.limit<=50)||
3
3
  lookup.minLength!==undefined&&!(Number.isInteger(lookup.minLength)&&lookup.minLength>=0&&lookup.minLength<=16)))editBad(id,"has invalid lookup settings");return Object.freeze({id,column,type,editor:gridFreeze({...raw,type}),options:editOptions(raw.options,id),lookup:lookup&&Object.freeze({column:lookup.column??id,limit:lookup.limit??20,minLength:lookup.minLength??1}),pattern,rules:checks})}
4
4
  function editCell(original,value,text){if(!isCellObject(original)&&text===undefined)return value;const cell=isCellObject(original)?{...original}:{};delete cell.text;delete cell.searchText;delete cell.sort;delete cell.secondary;cell.value=value;if(text!==undefined)cell.text=text;return cell}
@@ -84,7 +84,7 @@ const options=popup?.matches(":popover-open")?popup.getBoundingClientRect():box;
84
84
  if(!state.barEl)return;const entries=this.editEntries(),count=entries.length,errors=entries.filter(editInvalid).length,saving=[...state.changes.values()].some(entry=>entry.status==="saving");state.barEl.hidden=!count&&!state.undo.length&&!state.redo.length&&!saving;setText(state.barText,saving?state.labels.gridEditSaving:fill(state.labels.gridEditChanges,{count:numberFormat(this.lang).format(count)}));setText(state.barErrors,errors&&!saving?" · "+fill(state.labels.gridEditErrors,{count:numberFormat(this.lang).format(errors)}):"");state.undoButton.disabled=!state.undo.length;state.redoButton.disabled=!state.redo.length;state.discardButton.disabled=state.saveButton.disabled=!count||saving},editRemember(column,option){const seen=(this.edit.seen||=new Map()),list=seen.get(column)||[];if(!list.some(item=>item.value===option.value)){list.push(option);if(list.length>1000)list.shift()}
85
85
  seen.set(column,list)},async editLookup(spec,search,record,signal){const limit=spec.lookup.limit,lookups=this.edit.options.lookups,own=lookups&&Object.hasOwn(lookups,spec.id)?lookups[spec.id]:null,text=foldText(search.trim());const matching=list=>list.filter(option=>foldText(option.label).includes(text)).slice(0,limit);if(typeof own==="function")return editOptions(await own({column:spec.id,search,limit,row:editCopy(record),signal}),spec.id).slice(0,limit);if(spec.options)return matching(spec.options);const column=this.columns.find(item=>item.id===spec.lookup.column);if(this.view.source.kind==="inline"){await this.source.decoded;const values=new Map();for(const row of this.view.rows){const value=cellValue(row.cells[column.id]);if(value!=null&&!values.has(value))values.set(value,{value,label:cellText(row.cells[column.id],column,this.fmt)})}
86
86
  return matching([...values.values()].sort((left,right)=>left.label.localeCompare(right.label,this.lang)))}
87
- if(typeof this.source.facet!=="function"||!this.acceptedQuery)return[];const query=gridQuery(this.view,{...this.acceptedQuery,search:"",filters:[],filterGroup:undefined});const request=editCopy({protocol:PROTOCOL,type:"gridFacetQuery",query,column:column.id,search,limit,includeCurrentFilter:false});const response=await this.runGridHook(hook=>this.source.facet(request,{signal:hook}),signal);const page=validateGridFacetPage(response,this.view,request,{total:this.snapshot.total,matched:this.snapshot.total});return page.items.filter(item=>item.value!=null).map(item=>({value:item.value,label:cellText(item.value,column,this.fmt)}))}});const EDIT_FILL_ROW=DataGrid.prototype.fillRow;DataGrid.prototype.fillRow=function(element,row){EDIT_FILL_ROW.call(this,element,row);if(this.edit&&!row.status)this.paintEdits(element,row)};const editDrawerOn=grid=>{const state=grid.edit;return!!state&&state.specs.size>0&&state.drawer!=="never"&&(state.drawer!=="phone"||grid.phone)};DataGrid.drawerHook(10,function(dialog){dialog.addEventListener("cx-drawer-paint",()=>{const body=dialog.querySelector(".cx-drawer-body");if(body&&dialog.isConnected&&editDrawerOn(this))this.editForm(dialog,body,dialog._cxRecord?.id)});dialog.addEventListener("close",()=>{if(this.edit?.form?.dialog===dialog)this.editFormDispose()},{once:true})});Object.assign(DataGrid.prototype,{editFormDispose(){const form=this.edit?.form;if(form){form.fields.forEach(field=>this.editDispose(field));this.edit.form=null}},recordKeepField(id,record){return editDrawerOn(this)&&!this.editReadOnly&&this.edit.specs.has(id)&&!!record&&!!this.editRecord(record.id)&&this.canEditCell(this.editRecord(record.id),id)},editForm(dialog,body,id){const state=this.edit,record=this.editRecord(id),labels=state.labels;const kept=state.form&&state.form.dialog===dialog&&state.form.record.id===id&&record?state.form:null;if(!kept)this.editFormDispose();if(!record||this.editReadOnly||this.destroyed){this.editFormDispose();return}
87
+ if(typeof this.source.facet!=="function"||!this.acceptedQuery)return[];const query=gridQuery(this.view,{...this.acceptedQuery,search:"",filters:[],filterGroup:undefined,tupleFilters:undefined});const request=editCopy({protocol:PROTOCOL,type:"gridFacetQuery",query,column:column.id,search,limit,includeCurrentFilter:false});const response=await this.runGridHook(hook=>this.source.facet(request,{signal:hook}),signal);const page=validateGridFacetPage(response,this.view,request,{total:this.snapshot.total,matched:this.snapshot.total});return page.items.filter(item=>item.value!=null).map(item=>({value:item.value,label:cellText(item.value,column,this.fmt)}))}});const EDIT_FILL_ROW=DataGrid.prototype.fillRow;DataGrid.prototype.fillRow=function(element,row){EDIT_FILL_ROW.call(this,element,row);if(this.edit&&!row.status)this.paintEdits(element,row)};const editDrawerOn=grid=>{const state=grid.edit;return!!state&&state.specs.size>0&&state.drawer!=="never"&&(state.drawer!=="phone"||grid.phone)};DataGrid.drawerHook(10,function(dialog){dialog.addEventListener("cx-drawer-paint",()=>{const body=dialog.querySelector(".cx-drawer-body");if(body&&dialog.isConnected&&editDrawerOn(this))this.editForm(dialog,body,dialog._cxRecord?.id)});dialog.addEventListener("close",()=>{if(this.edit?.form?.dialog===dialog)this.editFormDispose()},{once:true})});Object.assign(DataGrid.prototype,{editFormDispose(){const form=this.edit?.form;if(form){form.fields.forEach(field=>this.editDispose(field));this.edit.form=null}},recordKeepField(id,record){return editDrawerOn(this)&&!this.editReadOnly&&this.edit.specs.has(id)&&!!record&&!!this.editRecord(record.id)&&this.canEditCell(this.editRecord(record.id),id)},editForm(dialog,body,id){const state=this.edit,record=this.editRecord(id),labels=state.labels;const kept=state.form&&state.form.dialog===dialog&&state.form.record.id===id&&record?state.form:null;if(!kept)this.editFormDispose();if(!record||this.editReadOnly||this.destroyed){this.editFormDispose();return}
88
88
  const form=kept||(state.form={dialog,record,fields:[]}),existing=new Map(form.fields.map(field=>[field.spec.id,field]));for(const{column,term,value}of this.drawerFields(dialog)){if(!column||value.tagName!=="DD"||!state.specs.has(column.id)||!this.canEditCell(record,column.id))continue;let field=existing.get(column.id);if(!field){field=this.editCreate(record,state.specs.get(column.id),"form");field.message=h("div","cx-edit-message");field.message.id=this.id+"-em" + ++state.sequence;field.message.hidden=true;form.fields.push(field)}
89
89
  field.label=term;if(field.host.parentNode!==value||field.message.parentNode!==value)value.replaceChildren(field.host,field.message)}
90
90
  dialog.classList.toggle("cx-edit-form",form.fields.length>0);let foot=dialog.querySelector(".cx-drawer-f");if(!form.fields.length){foot?.remove();return}
@@ -1,4 +1,4 @@
1
- (function(global){"use strict";const installed=new WeakSet();const part={version:"a101e32b0f8a1644",install(context){if(installed.has(context.GRID_ENHANCE_IMPL))return;const{DataGrid,GRID_ENHANCE_IMPL,PROTOCOL,createDataGrid,h,setText,gridQueryRows,gridCompare,gridResultShape,isIdentifier,GRID_REPORTING,loadPart,GRID_PAGER}=context;const ENHANCE_KINDS={text:"text",number:"number",datetime:"datetime",date:"datetime"};const ENHANCE_DATE=/^(\d{4}-\d\d-\d\d)(?:[T ](\d\d:\d\d(?::\d\d(?:\.\d{1,9})?)?)(Z|[+-]\d\d:?\d\d)?)?$/;
1
+ (function(global){"use strict";const installed=new WeakSet();const part={version:"a3b4be5ec19a43a6",install(context){if(installed.has(context.GRID_ENHANCE_IMPL))return;const{DataGrid,GRID_ENHANCE_IMPL,PROTOCOL,createDataGrid,h,setText,gridQueryRows,gridCompare,gridResultShape,isIdentifier,GRID_REPORTING,loadPart,GRID_PAGER}=context;const ENHANCE_KINDS={text:"text",number:"number",datetime:"datetime",date:"datetime"};const ENHANCE_DATE=/^(\d{4}-\d\d-\d\d)(?:[T ](\d\d:\d\d(?::\d\d(?:\.\d{1,9})?)?)(Z|[+-]\d\d:?\d\d)?)?$/;
2
2
  const ENHANCE_NUMBER=/^([^\p{L}\d\s+\-\u2212.,()]{0,3})\s?([+\-\u2212]?)(\(?)(\d[\d\s'\u2019.,\u00a0\u202f]*?)(\)?)(?:\s?[^\p{L}\d\s()]{1,3}|\s\p{L}{1,3})?$/u;
3
3
  let enhanceDecimal=null;function enhanceDecimalMark(){if(enhanceDecimal===null){try{enhanceDecimal=new Intl.NumberFormat(document.documentElement.lang||undefined).formatToParts(1.5).find(part=>part.type==="decimal")?.value||"."}
4
4
  catch(_){enhanceDecimal="."}}
@@ -94,17 +94,17 @@ return{url,params:settings.url==="hash"?new URLSearchParams(fragment):url.search
94
94
  async function enhanceLoadState(settings){let envelope=null;try{const pending=ENHANCE_STATE_WRITES.get(settings.key);if(pending)await pending.done;envelope=settings.load?await settings.load({key:settings.key,view:settings.view}):JSON.parse(enhanceStorage(settings)?.getItem(settings.key)||"null")}catch(_){envelope=null}
95
95
  if(!envelope||typeof envelope!=="object"||settings.duration&&!(Number.isFinite(envelope.time)&&Date.now()- envelope.time<=settings.duration*1000))envelope=null;let shared=null;if(settings.url){try{shared=JSON.parse(enhanceAddress(settings)?.params.get(settings.parameter)||"null")}catch(_){shared=null}}
96
96
  if(shared&&typeof shared==="object"&&!Array.isArray(shared)){const base=envelope&&envelope.current&&typeof envelope.current==="object"?envelope.current:
97
- {version:1,view:settings.view,columns:[],timeZone:settings.timeZone,selection:[],scrollTop:0,query:{}};const sort=Array.isArray(shared.s)?shared.s.filter(Array.isArray).map(([column,direction])=>({column,direction})):[];const query={...base.query,sort,search:typeof shared.q==="string"?shared.q:"",filters:Array.isArray(shared.f)?shared.f:[]};if(shared.g&&typeof shared.g==="object")query.filterGroup=shared.g;else delete query.filterGroup;const paging={pageLength:Number.isInteger(shared.l)?shared.l:base.paging?.pageLength||settings.pageLength||50,offset:Number.isInteger(shared.o)?shared.o:0};if(shared.a===true)paging.allRows=true;envelope={...envelope,current:{...base,query,paging,scrollTop:0}}}
97
+ {version:1,view:settings.view,columns:[],timeZone:settings.timeZone,selection:[],scrollTop:0,query:{}};const sort=Array.isArray(shared.s)?shared.s.filter(Array.isArray).map(([column,direction])=>({column,direction})):[];const query={...base.query,sort,search:typeof shared.q==="string"?shared.q:"",filters:Array.isArray(shared.f)?shared.f:[]};if(shared.g&&typeof shared.g==="object")query.filterGroup=shared.g;else delete query.filterGroup;if(Array.isArray(shared.t)&&shared.t.length)query.tupleFilters=shared.t;else delete query.tupleFilters;const paging={pageLength:Number.isInteger(shared.l)?shared.l:base.paging?.pageLength||settings.pageLength||50,offset:Number.isInteger(shared.o)?shared.o:0};if(shared.a===true)paging.allRows=true;envelope={...envelope,current:{...base,query,paging,scrollTop:0}}}
98
98
  return envelope}
99
99
  function enhanceSaveState(settings,envelope){envelope.time=Date.now();try{if(settings.save)enhanceWriteState(settings,envelope);else enhanceStorage(settings)?.setItem(settings.key,JSON.stringify(envelope))}catch(_){/* A full or blocked store leaves the grid usable. */ }
100
- if(!settings.url)return;const query=envelope.current.query,paging=envelope.current.paging||{},shared={};if(query.sort.length)shared.s=query.sort.map(sort=>[sort.column,sort.direction]);if(query.search)shared.q=query.search;if(query.filters.length)shared.f=query.filters;if(query.filterGroup)shared.g=query.filterGroup;if(paging.offset)shared.o=paging.offset;if(paging.pageLength&&paging.pageLength!==settings.pageLength)shared.l=paging.pageLength;if(paging.allRows)shared.a=true;const address=enhanceAddress(settings);if(!address)return;const{url,params}=address,text=Object.keys(shared).length?JSON.stringify(shared):null;if(params.get(settings.parameter)===text)return;if(text===null)params.delete(settings.parameter);else params.set(settings.parameter,text);if(settings.url==="hash")url.hash=params.toString();try{global.history.replaceState(global.history.state,"",url.href)}catch(_){/* Sandboxed documents cannot change their address. */ }
100
+ if(!settings.url)return;const query=envelope.current.query,paging=envelope.current.paging||{},shared={};if(query.sort.length)shared.s=query.sort.map(sort=>[sort.column,sort.direction]);if(query.search)shared.q=query.search;if(query.filters.length)shared.f=query.filters;if(query.filterGroup)shared.g=query.filterGroup;if(query.tupleFilters?.length)shared.t=query.tupleFilters;if(paging.offset)shared.o=paging.offset;if(paging.pageLength&&paging.pageLength!==settings.pageLength)shared.l=paging.pageLength;if(paging.allRows)shared.a=true;const address=enhanceAddress(settings);if(!address)return;const{url,params}=address,text=Object.keys(shared).length?JSON.stringify(shared):null;if(params.get(settings.parameter)===text)return;if(text===null)params.delete(settings.parameter);else params.set(settings.parameter,text);if(settings.url==="hash")url.hash=params.toString();try{global.history.replaceState(global.history.state,"",url.href)}catch(_){/* Sandboxed documents cannot change their address. */ }
101
101
  }
102
102
  function enhanceStateStore(grid,settings,envelope){grid.readStoredState=()=>envelope;grid.writeStoredState=function(state){if(this.destroyed)return;enhanceSaveState(settings,{current:state,views:[...this.savedViews].map(([name,value])=>({name,state:value}))})}}
103
103
  const ENHANCE_OPTIONS=["id","label","noun","columns","data","url","serverSide","rowId","paging","pageLength","pageLengths","lengthMenu","info","pageNumbers","pageInput","firstLast","search","searchText","searchMode","order","sortable","selection","stateSave","texts","markup","footer","height","theme","density","zebra","view","on"];const ENHANCE_TEXTS={search:"searchPlaceholder",info:"gridPageInfo",infoFiltered:"gridPageInfoFiltered",lengthMenu:"gridPageLength",first:"gridFirstPage",previous:"gridPreviousPage",next:"gridNextPage",last:"gridLastPage",page:"gridPageButton",all:"gridAll",empty:"gridEmpty",emptyReason:"gridEmptyReason",noMatches:"gridNoMatches",loading:"loading",loadFailed:"gridLoadFailed",retry:"gridRetry"};const ENHANCE_DERIVED=["init","draw","order","search","page","length"];const ENHANCE_SETUP=new WeakMap(),ENHANCE_CONTROLLERS=new WeakMap(),ENHANCE_PENDING=new WeakMap();let enhanceSeq=0;const ENHANCE_PREPARE=DataGrid.prototype.prepareState;DataGrid.prototype.prepareState=function(){const setup=ENHANCE_SETUP.get(this.opts);if(setup)setup(this);return ENHANCE_PREPARE.call(this)};function enhanceOrder(order,columns){if(order===undefined||order===null)return[];if(!Array.isArray(order))throw new Error("CanopyX: order is a list of [column, direction] pairs.");return order.map(entry=>{const[reference,direction="asc"]=Array.isArray(entry)?entry:[entry&&entry.column,entry&&entry.direction];const column=typeof reference==="number"?columns[reference]:columns.find(item=>item.grid.id===reference);if(!column||!column.grid.sortable||!["asc","desc"].includes(direction))throw new Error("CanopyX: order names an unknown or unsortable column.");return{column:column.grid.id,direction}})}
104
104
  function enhancePager(settings){const pager={lengths:settings.pageLengths===undefined?[10,25,50,100,"all"]:settings.pageLengths};const numbers=settings.pageNumbers===undefined||settings.pageNumbers===true?7:settings.pageNumbers===false?0:settings.pageNumbers;if(numbers)pager.numbers=numbers;for(const name of["lengthMenu","pageInput","firstLast"])if(settings[name]!==undefined)pager[name]=settings[name];pager.info=settings.info===false?"none":settings.info===undefined||settings.info===true?"sentence":settings.info;return pager}
105
105
  function enhanceLabels(settings){const labels={};if(settings.texts&&typeof settings.texts==="object")
106
106
  for(const[name,text]of Object.entries(settings.texts))if(Object.hasOwn(ENHANCE_TEXTS,name)&&typeof text==="string")labels[ENHANCE_TEXTS[name]]=text;return Object.assign(labels,settings.labels)}
107
- const ENHANCE_STYLES={caption:"flex:none;padding:var(--cx-size-10px) var(--cx-size-12px) var(--cx-size-2px);color:var(--hfx-color-text,light-dark(#16181c,#f2f3f5));font-size:var(--cx-size-14px);font-weight:600;line-height:1.35",foot:"flex:none;display:flex;flex-direction:column;gap:var(--cx-size-4px);padding:var(--cx-size-8px) var(--cx-size-12px);border-top:var(--cx-size-1px) solid var(--hfx-color-border,light-dark(#e2e4e7,#272a30));color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));font-size:var(--cx-size-13px)",row:"display:flex;flex-wrap:wrap;gap:var(--cx-size-4px) var(--cx-size-16px);margin:0",value:"color:var(--hfx-color-text,light-dark(#16181c,#f2f3f5));font-weight:600;font-variant-numeric:tabular-nums"};function enhanceStyled(tag,className,style){const element=h(tag,className);element.style.cssText=ENHANCE_STYLES[style];return element}
107
+ const ENHANCE_STYLES={caption:"flex:none;padding:var(--cx-size-10px) var(--cx-size-12px) var(--cx-size-2px);color:var(--hfx-color-text-primary,var(--hfx-color-text,light-dark(#16181c,#f2f3f5)));font-size:var(--cx-size-14px);font-weight:600;line-height:1.35",foot:"flex:none;display:flex;flex-direction:column;gap:var(--cx-size-4px);padding:var(--cx-size-8px) var(--cx-size-12px);border-top:var(--cx-size-1px) solid var(--hfx-color-surface-line,var(--hfx-color-border,light-dark(#e2e4e7,#272a30)));color:var(--hfx-color-text-secondary,var(--hfx-color-text-muted,light-dark(#626770,#8f949d)));font-size:var(--cx-size-13px)",row:"display:flex;flex-wrap:wrap;gap:var(--cx-size-4px) var(--cx-size-16px);margin:0",value:"color:var(--hfx-color-text-primary,var(--hfx-color-text,light-dark(#16181c,#f2f3f5)));font-weight:600;font-variant-numeric:tabular-nums"};function enhanceStyled(tag,className,style){const element=h(tag,className);element.style.cssText=ENHANCE_STYLES[style];return element}
108
108
  function enhanceFooter(rows,columns){const foot=enhanceStyled("div","cx-enhanced-foot","foot");for(const values of rows){const line=enhanceStyled("p","cx-enhanced-foot-row","row");values.forEach((text,index)=>{if(!text)return;const item=h("span"),title=index&&columns[index]?columns[index].grid.title:"",value=enhanceStyled("b","","value");if(title){const name=h("span");setText(name,title+" ");item.appendChild(name)}
109
109
  setText(value,text);item.appendChild(value);line.appendChild(item)});if(line.firstChild)foot.appendChild(line)}
110
110
  return foot.firstChild?foot:null}
@@ -118,7 +118,7 @@ const footer=read&&settings.footer!==false&&!(settings.view&&settings.view.repor
118
118
  function enhanceEvents(host,initial){const handlers=new Map(),listening=new Set(),lifetime=new AbortController();let controller=null,api=null,previous=null,initialized=false;const call=(handler,detail,event)=>{try{Promise.resolve(handler.call(controller,detail,controller,event)).catch(error=>global.console?.error(error))}
119
119
  catch(error){global.console?.error(error)}};const emit=(name,detail,event)=>{for(const handler of[...(handlers.get(name)||[])])call(handler,detail,event)};const on=(name,handler)=>{if(typeof name!=="string"||!/^[A-Za-z][\w-]*$/.test(name) || typeof handler !== "function") throw new Error("CanopyX: on takes an event name and a function.");
120
120
  (handlers.get(name)||handlers.set(name,new Set()).get(name)).add(handler);if(!ENHANCE_DERIVED.includes(name)&&!listening.has(name)){listening.add(name);host.addEventListener("canopyx:"+name,event=>emit(name,event.detail,event),{signal:lifetime.signal})}
121
- if(name==="init"&&initialized)Promise.resolve().then(()=>{if(!lifetime.signal.aborted&&handlers.get(name)?.has(handler))call(handler,{query:api.getQuery()})})};host.addEventListener("canopyx:query",event=>{if(!api)return;const query=event.detail.query,paging=api.getPaging(),before=previous,detail={query,paging};const same=(left,right)=>JSON.stringify(left===undefined?null:left)===JSON.stringify(right===undefined?null:right);previous=detail;if(before){if(!same(before.query.sort,query.sort))emit("order",detail,event);if(before.query.search!==query.search||!same([before.query.filters,before.query.filterGroup],[query.filters,query.filterGroup]))emit("search",detail,event);if(before.paging.pageLength!==paging.pageLength)emit("length",detail,event);if(before.paging.pageIndex!==paging.pageIndex)emit("page",detail,event)}
121
+ if(name==="init"&&initialized)Promise.resolve().then(()=>{if(!lifetime.signal.aborted&&handlers.get(name)?.has(handler))call(handler,{query:api.getQuery()})})};host.addEventListener("canopyx:query",event=>{if(!api)return;const query=event.detail.query,paging=api.getPaging(),before=previous,detail={query,paging};const same=(left,right)=>JSON.stringify(left===undefined?null:left)===JSON.stringify(right===undefined?null:right);previous=detail;if(before){if(!same(before.query.sort,query.sort))emit("order",detail,event);if(before.query.search!==query.search||!same([before.query.filters,before.query.filterGroup,before.query.tupleFilters],[query.filters,query.filterGroup,query.tupleFilters]))emit("search",detail,event);if(before.paging.pageLength!==paging.pageLength)emit("length",detail,event);if(before.paging.pageIndex!==paging.pageIndex)emit("page",detail,event)}
122
122
  emit("draw",detail,event)},{signal:lifetime.signal});try{if(initial!==undefined){if(initial===null||typeof initial!=="object")throw new Error("CanopyX: on is an object of handlers by event name.");for(const[name,handler]of Object.entries(initial))on(name,handler)}}catch(error){lifetime.abort();throw error}
123
123
  return{on,off:(name,handler)=>{handlers.get(name)?.delete(handler)},abort:()=>lifetime.abort(),start(created,grid){controller=created;api=grid;api.ready.then(query=>{if(lifetime.signal.aborted)return;initialized=true;emit("init",{query})},()=>{})}}}
124
124
  function enhanceController({api,element,host,isTable,read,columns,store,rowSource,server,events,settings,caption,footer,restore}){let destroyed=false;const recordOf=row=>store.records&&store.records.has(row.id)?store.records.get(row.id):store.tableRecord?store.tableRecord(row.id):undefined;const local=name=>{if(server)throw new Error("CanopyX: "+name+" changes inline data; a server source changes on the server, then reload().")};const refresh=()=>{store.version++;if(store.grid)store.grid.view.rows=store.rows;const query=api.getQuery();if(!query)return api.reload();const request={...query,revision:undefined};return store.grid&&store.grid.pageAll?store.grid.setQuery({...request,offset:0},{pageLength:Math.max(1,store.rows.length),allRows:true}):api.setQuery(request)};const own={table:isTable?element:null,headerGroups:read?enhanceHeaderGroups(read,columns.map(column=>column.grid.id)):[],on(name,handler){events.on(name,handler);return controller},off(name,handler){events.off(name,handler);return controller},getRows(scope="all"){if(!["all","filtered","page"].includes(scope))throw new Error("CanopyX: getRows takes all, filtered or page.");const query=api.getQuery();let rows=store.rows;if(!server&&scope!=="all"){rows=query?rowSource.order(query):[];if(scope==="page"){const paging=api.getPaging();rows=rows.slice(Math.max(0,paging.first-1),paging.last)}}
@@ -1,22 +1,63 @@
1
- (function(global){"use strict";const installed=new WeakSet();const part={version:"9203882f58fa0155",install(context){if(installed.has(context.EXPORT_IMPL))return;const{DataGrid,EXPORT_IMPL,h,setText,gridButton,gridOption,fill,numberFormat,gridOperationActive,formatting,gridQuery,validateGridCursorPage,validateGridPage,GRID_REPORTING,gridFreeze,cellValue,cellText,isCellObject,exportTimeout,exportFormat,EXPORT_FORMATS,gridExportField}=context;function saveExport(blob,name){const url=URL.createObjectURL(blob),link=h("a");link.href=url;link.download=String(name).replace(/[\\/:*?"<>|\x00-\x1f\x7f\u200e\u200f\u202a-\u202e\u2066-\u2069]/g, "_").replace(/^[. ]+|[. ]+$/g, "_");
1
+ (function(global){"use strict";const installed=new WeakSet();const part={version:"15abf3205094bda6",requires:["ui-overlays"],install(context){if(installed.has(context.EXPORT_IMPL))return;const{DataGrid,EXPORT_IMPL,h,setText,gridButton,gridOption,fill,numberFormat,gridOperationActive,formatting,gridQuery,validateGridCursorPage,validateGridPage,GRID_REPORTING,gridFreeze,cellValue,cellText,isCellObject,exportTimeout,exportFormat,EXPORT_FORMATS,gridExportField}=context;function printContext(grid,snapshot){const{request,rows}=snapshot,labels=grid.labels,query=request.query,locale=request.locale||grid.lang,scope=labels[request.scope==="all"?"gridAllResults":request.scope==="selected"?"gridSelectedRows":"gridFilteredResults"],parts=[scope,numberFormat(locale).format(rows.length)+ " "+labels.gridRows];if(request.grouping?.levels.length)parts.push(labels.gridGroupedBy+" "+request.grouping.levels.map(level=>level.title).join(", "));if(query?.search)parts.push(labels.searchPlaceholder+": “"+query.search+"”");const filter=item=>{const column=request.columns.find(column=>column.id===item.column),value=item.values??item.value;const operator={eq:"=",ne:"≠",gt:">",ge:"≥",lt:"<",le:"≤",contains:labels.gridContains,startsWith:labels.gridStartsWith,in:labels.gridIn,isNull:labels.gridIsNull,isNotNull:labels.gridIsNotNull}[item.operator]||item.operator;return(column?.title||item.column)+ " "+operator +(value==null?"":" " +(Array.isArray(value)?value.join(", "):String(value)))};const group=value=>"(" +[...(value.filters||[]).map(filter),...(value.groups||[]).map(group)].join(value.operator==="or"?" ∨ ":" ∧ ")+ ")";const filters=[...(query?.filters||[]).map(filter),...(query?.filterGroup?[group(query.filterGroup)]:[])].filter(Boolean);if(filters.length)parts.push(labels.gridFilters+": "+filters.join(" · "));const time=new Date(request.generatedAt||Date.now());parts.push(cellText(time.toISOString(),{kind:"datetime"},formatting(locale,request.timeZone==="local",labels)));return parts.join(" · ")}
2
+ EXPORT_IMPL.snapshot=async function(grid,capture,maxRows,signal,progress=()=>{}){if(!Number.isInteger(maxRows)||maxRows<1||maxRows>10000)throw new Error("CanopyX: print snapshots support 1 to 10000 records.");if(capture.request.recordCount!==null&&capture.request.recordCount>maxRows)
3
+ throw new Error("CanopyX: snapshot exceeds its row bound; use a host export hook.");const rows=[];let size=JSON.stringify(capture.request).length;if(size>4194304)throw new Error("CanopyX: snapshot exceeds its text bound; use a host export hook.");progress("reading",0);for await(const row of grid.exportRows(capture.request,signal)){if(rows.length>=maxRows||(size +=JSON.stringify(row).length+1)>4194304)
4
+ throw new Error("CanopyX: snapshot exceeds its row or text bound; use a host export hook.");rows.push(row);if(rows.length%100===0)progress("reading",rows.length)}
5
+ gridOperationActive(grid,signal);progress("generating",rows.length);return gridFreeze({request:capture.request,rows})};EXPORT_IMPL.printSnapshot=async function(grid,snapshot,signal,layout,templates,projection,cardRoles){gridOperationActive(grid,signal);for(const row of snapshot.rows)if(row.detail!=null){const detail=row.detail;if(detail.type==="html")throw new Error("CanopyX: HTML print details require an onPrint hook or includeDetails: false.");if(detail.type==="table"?!Array.isArray(detail.columns)||!Array.isArray(detail.rows)||
6
+ detail.rows.some(cells=>!Array.isArray(cells)||cells.length!==detail.columns.length):
7
+ detail.type!=="properties"||!Array.isArray(detail.items))throw new Error("CanopyX: invalid static print detail payload.")}
8
+ const frame=document.createElement("iframe");frame.title=grid.labels.gridPrint;frame.style.cssText="position:fixed;width:1px;height:1px;inset:0;border:0;clip-path:inset(100%)";document.body.appendChild(frame);const dispose=()=>frame.remove();signal?.addEventListener("abort",dispose,{once:true});try{const doc=frame.contentDocument,columns=projection?snapshot.request.columns.filter(column=>projection.includes(column.id)):snapshot.request.columns;doc.documentElement.lang=snapshot.request.locale||grid.lang||"en";doc.title=grid.view.label;doc.documentElement.style.colorScheme="light";const theme=getComputedStyle(grid.host);for(const name of theme)if(/^--hfx-(?:font|type|weight)-/.test(name)) doc.documentElement.style.setProperty(name, theme.getPropertyValue(name));
9
+ const add=(tag,parent,text,css)=>{const element=doc.createElement(tag);if(text!==undefined)element.textContent=String(text);if(css)element.style.cssText=css;parent.appendChild(element);return element};add("style",doc.head,"@page{size:A4 " +(layout!=="stacked"&&columns.length>6?"landscape":"portrait")+ ';margin:16mm 14mm;@bottom-right{content:counter(page);font:var(--hfx-type-caption-size,0.75rem) var(--hfx-font-sans,"Segoe UI Variable Text", "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif);color:var(--hfx-color-text-secondary,var(--hfx-color-text-muted,light-dark(#626770,#8f949d)))}}' +
10
+ "thead{display:table-header-group}tfoot{display:table-row-group}tr{break-inside:avoid}a{color:inherit;text-decoration:none}" +
11
+ "@media screen{body{padding:40px}main{max-width:1100px;margin:auto}}@media screen and (max-width:600px){body{padding:20px}}" +
12
+ "@media (max-width:600px){body{font-size:10px!important}td,th{padding:6px 4px!important;white-space:normal!important}td.cx-print-number{white-space:nowrap!important}col.cx-print-date{width:80px!important}}" +
13
+ ".cx-print-cards{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;align-items:start}" +
14
+ ".cx-print-card{break-inside:avoid;min-width:0;border:1px solid var(--hfx-color-surface-line,var(--hfx-color-border,light-dark(#e2e4e7,#272a30)));border-radius:var(--hfx-radius-container,var(--hfx-radius-lg,10px));padding:16px}" +
15
+ ".cx-print-card-heading{display:flex;align-items:baseline;flex-wrap:wrap;gap:8px;margin:0 0 14px}" +
16
+ ".cx-print-card h2{flex:1;margin:0;font-size:16px;line-height:1.3;overflow-wrap:anywhere}" +
17
+ ".cx-print-card-status{border:1px solid var(--hfx-color-border-strong,light-dark(#cdd0d5,#363a42));border-radius:var(--hfx-radius-pill,999px);padding:2px 7px;font-size:10px}" +
18
+ ".cx-print-card dl{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px 16px;margin:0}" +
19
+ ".cx-print-card dt{color:var(--hfx-color-text-secondary,var(--hfx-color-text-muted,light-dark(#626770,#8f949d)));font-size:10px;margin:0 0 3px}.cx-print-card dd{margin:0;overflow-wrap:anywhere}" +
20
+ ".cx-print-card footer{border-top:1px solid var(--hfx-color-surface-line,var(--hfx-color-border,light-dark(#e2e4e7,#272a30)));padding-top:10px;margin-top:14px;color:var(--hfx-color-text-secondary,var(--hfx-color-text-muted,light-dark(#626770,#8f949d)))}" +
21
+ ".cx-print-card footer span+span{margin-inline-start:6px}.cx-print-card-wide,.cx-print-cards>h2,.cx-print-cards>[data-cx-print-subtotal],.cx-print-cards>[data-cx-print-total]{grid-column:1/-1}" +
22
+ "@media screen and (min-width:1200px){.cx-print-cards{grid-template-columns:repeat(3,minmax(0,1fr))}}" +
23
+ "@media screen and (max-width:600px){.cx-print-cards{grid-template-columns:minmax(0,1fr)}}" +
24
+ "@media print{body{padding:0}h1{break-after:avoid}header{break-after:avoid}}");doc.body.style.cssText='margin:0;background:var(--hfx-color-surface-card,var(--hfx-color-surface,light-dark(#ffffff,#16181c)));color:var(--hfx-color-text-primary,var(--hfx-color-text,light-dark(#16181c,#f2f3f5)));font:11px/1.4 var(--hfx-font-sans,"Segoe UI Variable Text", "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif)';const main=add("main",doc.body),reportHeader=add("header",main,undefined,"margin-bottom:22px;padding-bottom:14px;border-bottom:2px solid var(--hfx-color-border-strong,light-dark(#cdd0d5,#363a42))");add("h1",reportHeader,grid.view.label,"font-size:26px;letter-spacing:-.5px;line-height:1.2;margin:0 0 8px");add("p",reportHeader,printContext(grid,snapshot),"margin:0;max-width:90ch;color:var(--hfx-color-text-secondary,var(--hfx-color-text-muted,light-dark(#626770,#8f949d)));font-size:11px;line-height:1.6");const cellStyle="padding:5px 8px;border-bottom:1px solid var(--hfx-color-surface-line,var(--hfx-color-border,light-dark(#e2e4e7,#272a30)));text-align:left;vertical-align:top;overflow-wrap:anywhere";const value=(parent,cell)=>{const text=cell&&typeof cell==="object"?cell.text??cell.value??"":cell??"";const link=cell&&typeof cell==="object"&&cell.link?.target;if(typeof link==="string"&&/^(?:https?:|mailto:)/i.test(link)) {
25
+ const anchor=add("a",parent,text,"color:inherit;text-decoration:none");anchor.href=link;if(cell.link.tooltip)anchor.title=cell.link.tooltip}else parent.textContent=String(text)};const detail=(parent,payload)=>{parent.setAttribute("data-cx-print-detail","");if(payload.type==="table"){const table=add("table",parent,undefined,"border-collapse:collapse;width:100%;font:inherit");if(payload.caption)add("caption",table,payload.caption,"text-align:left;font-weight:var(--hfx-weight-semibold,600);margin:4px 0");const headings=add("tr",add("thead",table));for(const column of payload.columns)add("th",headings,column,cellStyle+";overflow-wrap:normal");const body=add("tbody",table);for(const cells of payload.rows){const row=add("tr",body);for(const cell of cells)value(add("td",row,undefined,cellStyle),cell)}}else{const list=add("dl",parent);for(const item of payload.items){add("dt",list,item.name,"font-weight:var(--hfx-weight-semibold,600)");value(add("dd",list,undefined,"margin:0 0 6px;overflow-wrap:anywhere"),item.value)}}};const cell=(row,column)=>{if(!templates||!Object.hasOwn(templates,column.id))return row.cells[column.id];const text=id=>{if(row.templateText&&Object.hasOwn(row.templateText,id))return row.templateText[id];const input=row.cells[id];return input&&typeof input==="object"?input.text??input.value??"":input??""};return{text:typeof templates[column.id]==="string"?templates[column.id].replace(/\{([^{}]+)\}/g, (_, id) => text(id)) : text(column.id) };
26
+ };const grouping=snapshot.request.grouping,starts=new Map(),ends=new Map(),fmt=formatting(snapshot.request.locale||grid.lang,snapshot.request.timeZone==="local",grid.labels);const collect=(groups,depth)=>{for(const group of groups||[]){const headings=starts.get(group.offset)||[];headings.push({text:grouping.levels[depth].title+": "+group.text,depth});starts.set(group.offset,headings);const footers=ends.get(group.offset+group.count)||[];footers.unshift({text:grouping.levels[depth].title+": "+group.text,values:group.aggregates,depth});ends.set(group.offset+group.count,footers);collect(group.groups,depth+1)}};if(grouping)collect(grouping.groups,0);if(layout==="stacked"){const cards=add("div",main);cards.className="cx-print-cards";const summary=(values,label,subtotal)=>{if(!values||!grouping?.aggregates.length)return;const section=add("div",cards,undefined,"break-inside:avoid;padding:8px 10px;margin:0 0 12px;background:var(--hfx-color-surface-sunken,light-dark(#f7f8f9,#111317));border-top:1px solid var(--hfx-color-surface-line,var(--hfx-color-border,light-dark(#e2e4e7,#272a30)));font-weight:var(--hfx-weight-semibold,600)");section.setAttribute(subtotal?"data-cx-print-subtotal":"data-cx-print-total","");add("div",section,label);for(const aggregate of grouping.aggregates){const column=columns.find(column=>column.id===aggregate.column);if(column)add("div",section,(aggregate.label||column.title)+ ": "+cellText(values[aggregate.id],column,fmt))}};for(const[index,row]of snapshot.rows.entries()){for(const heading of starts.get(index)||[])add("h2",cards,heading.text,"font-size:15px;margin:12px 0 6px");const section=add("section",cards);section.className="cx-print-card";section.setAttribute("data-cx-print-record","");const heading=add("div",section);heading.className="cx-print-card-heading";const list=add("dl",section);for(const[index,column]of columns.entries()){const role=cardRoles?.[index]||(index?"field":"title");if(role==="title")value(add("h2",heading),cell(row,column));else if(role==="pill"){const status=add("span",heading);status.className="cx-print-card-status";status.setAttribute("aria-label",column.title);value(status,cell(row,column))}else if(role==="footer"){const footer=add("footer",section);add("span",footer,column.title);value(add("span",footer),cell(row,column))}else{const field=add("div",list);if(role==="wide")field.className="cx-print-card-wide";add("dt",field,column.title);value(add("dd",field),cell(row,column))}}
27
+ if(!list.childNodes.length)list.remove();if(row.detail)detail(add("div",section,undefined,"margin-top:8px"),row.detail);for(const group of ends.get(index+1)||[])summary(group.values,group.text,true)}
28
+ summary(grouping?.totals,grid.labels.gridSummary,false)}else{const table=add("table",main,undefined,"border-collapse:collapse;width:100%;font:inherit"),head=add("thead",table),header=add("tr",head);const widths=add("colgroup",table);table.insertBefore(widths,head);for(const column of columns){const col=add("col",widths,undefined,column.kind==="number"?"width:1%":column.kind==="datetime"?"width:144px":undefined);if(column.kind==="datetime")col.className="cx-print-date";add("th",header,column.title,cellStyle+";overflow-wrap:normal;background:var(--hfx-color-surface-sunken,light-dark(#f7f8f9,#111317));font-weight:var(--hfx-weight-semibold,600);white-space:nowrap" +(column.kind==="number"?";text-align:right":""))}
29
+ const body=add("tbody",table);const summary=(parent,values,label,subtotal)=>{if(!values||!grouping?.aggregates.length)return;const tr=add("tr",parent);tr.setAttribute(subtotal?"data-cx-print-subtotal":"data-cx-print-total","");for(const[index,column]of columns.entries()){const summaries=grouping.aggregates.filter(aggregate=>aggregate.column===column.id),td=add("td",tr,summaries.length?undefined:index===0?label:"",cellStyle+";font-weight:var(--hfx-weight-semibold,600);background:var(--hfx-color-surface-sunken,light-dark(#f7f8f9,#111317))" +
30
+ (column.kind==="number"?";text-align:right":""));for(const aggregate of summaries){const pair=add("div",td);add("span",pair,(aggregate.label||aggregate.operation)+ ": ","display:block;color:var(--hfx-color-text-secondary,var(--hfx-color-text-muted,light-dark(#626770,#8f949d)));font-size:9px;font-weight:var(--hfx-weight-regular,400)");add("span",pair,cellText(values[aggregate.id],column,fmt),"display:block;white-space:nowrap")}}};for(const[index,row]of snapshot.rows.entries()){for(const heading of starts.get(index)||[]){const group=add("tr",body),title=add("th",group,heading.text,cellStyle+";background:var(--hfx-color-surface-sunken,light-dark(#f7f8f9,#111317))");title.colSpan=columns.length;title.style.paddingInlineStart=(8+heading.depth*12)+ "px"}
31
+ const tr=add("tr",body,undefined,"break-inside:avoid");tr.setAttribute("data-cx-print-record","");for(const column of columns){const td=add("td",tr,undefined,cellStyle);if(column.kind==="number"){td.className="cx-print-number";td.style.textAlign="right";td.style.whiteSpace="nowrap"}
32
+ value(td,cell(row,column))}
33
+ if(row.detail){const next=add("tr",body),holder=add("td",next,undefined,cellStyle);holder.colSpan=columns.length;detail(holder,row.detail)}
34
+ for(const group of ends.get(index+1)||[])summary(body,group.values,group.text,true)}
35
+ summary(add("tfoot",table),grouping?.totals,grid.labels.gridSummary,false)}
36
+ await doc.fonts.ready;await new Promise(resolve=>requestAnimationFrame(()=>requestAnimationFrame(resolve)));gridOperationActive(grid,signal);frame.contentWindow.focus();frame.contentWindow.print()}finally{signal?.removeEventListener("abort",dispose);dispose()}};function saveExport(blob,name){const url=URL.createObjectURL(blob),link=h("a");link.href=url;link.download=String(name).replace(/[\\/:*?"<>|\x00-\x1f\x7f\u200e\u200f\u202a-\u202e\u2066-\u2069]/g, "_").replace(/^[. ]+|[. ]+$/g, "_");
2
37
  document.body.appendChild(link);link.click();link.remove();setTimeout(()=>URL.revokeObjectURL(url),30000)}
3
- Object.assign(EXPORT_IMPL,{async run(grid,id,options,capture,format){if(!format)throw new Error("CanopyX: export format '"+id+"' is not available.");const total=capture.request.recordCount,onProgress=options.onProgress;if(onProgress!==undefined&&typeof onProgress!=="function"||options.download!==undefined&&typeof options.download!=="boolean")
4
- throw new Error("CanopyX: export progress is a callback and download is a boolean.");const timer=new AbortController(),clock=setTimeout(()=>timer.abort(new DOMException("Export timed out.","TimeoutError")),exportTimeout(grid,options));const signal=AbortSignal.any([grid.lifetime.signal,timer.signal,options.signal].filter(Boolean));const failure=()=>signal.reason?.name==="TimeoutError"?new Error("CanopyX: export timed out."):new DOMException("Export cancelled.","AbortError");const phases=["reading","generating","ready"];let phase=-1,done=0,read=0,stop;const report=(name,completed)=>{const next=phases.indexOf(name),count=total===null?completed:Math.min(completed,total);if(signal.aborted||next<phase||!Number.isSafeInteger(completed)||completed<0||next===phase&&count<=done)return;phase=next;done=Math.max(done,count);onProgress?.(Object.freeze({phase:name,completed:done,total}))};const stopped=new Promise((resolve,reject)=>{stop=()=>reject(failure())});signal.addEventListener("abort",stop,{once:true});try{if(signal.aborted)throw failure();const counted=Object.freeze({request:capture.request,rows:async function*(more){read=0;for await(const row of capture.rows({signal:AbortSignal.any([signal,more?.signal].filter(Boolean))})){yield row;if(++read%100===0)report("reading",read)}
5
- report("generating",read)}});const base=capture.request.fileName||grid.view.id.replace(/[^a-z0-9_-]/gi, "_");
6
- report("reading",0);let blob=await Promise.race([Promise.resolve().then(()=>format.write(counted,Object.freeze({signal,fileName:base,format:format.id,progress:completed=>report("generating",completed)}))),stopped]).catch(error=>{throw error instanceof Error||error instanceof DOMException?error:new Error("CanopyX: export format '"+id+"' failed: "+String(error))});if(signal.aborted)throw failure();if(blob instanceof ArrayBuffer||ArrayBuffer.isView(blob))blob=new Blob([blob],{type:format.mime});if(!(blob instanceof Blob))throw new Error("CanopyX: export format '"+id+"' must write a Blob.");const fileName=blob.name||base+"."+format.extension;if(options.download!==false)saveExport(blob,fileName);report("ready",read);return Object.freeze({blob,fileName,format:format.id})}finally{clearTimeout(clock);signal.removeEventListener("abort",stop)}},async*treeRows(grid,request,rows,signal){const fmt=formatting(request.locale,request.timeZone==="local",grid.labels);const columns=request.columns.map(column=>[column.id,grid.columns.findIndex(item=>item.id===column.id)]);for(const{id,level,path,node}of rows){if(grid.destroyed||signal.aborted)throw new DOMException("Grid cancelled.","AbortError");const cells=Object.create(null);for(const[key,index]of columns){const column=grid.columns[index],cell=grid.cellOf(node,column,index),tone=isCellObject(cell)&&cell.tone;cells[key]=Object.freeze({value:cellValue(cell)??null,text:cellText(cell,column,fmt),...(tone?{tone}:{})})}
38
+ Object.assign(EXPORT_IMPL,{async run(grid,id,options,capture,format){if(!format)throw new Error("CanopyX: export format '"+id+"' is not available.");const total=capture.request.recordCount,onProgress=options.onProgress,diagnostics=[];if(onProgress!==undefined&&typeof onProgress!=="function"||options.onDiagnostic!==undefined&&typeof options.onDiagnostic!=="function"||
39
+ options.download!==undefined&&typeof options.download!=="boolean"||options.includeMetadata!==undefined&&typeof options.includeMetadata!=="boolean")
40
+ throw new Error("CanopyX: export callbacks must be functions and download/includeMetadata must be booleans.");const timer=new AbortController(),clock=setTimeout(()=>timer.abort(new DOMException("Export timed out.","TimeoutError")),exportTimeout(grid,options));const signal=AbortSignal.any([grid.lifetime.signal,timer.signal,options.signal].filter(Boolean));const failure=()=>signal.reason?.name==="TimeoutError"?new Error("CanopyX: export timed out."):new DOMException("Export cancelled.","AbortError");const phases=["reading","generating","ready"];let phase=-1,done=0,read=0,inputsComplete=false,finished=false,stop;const report=(name,completed)=>{const next=phases.indexOf(name),count=total===null?completed:Math.min(completed,total);if(signal.aborted||next<phase||!Number.isSafeInteger(completed)||completed<0||next===phase&&count<=done)return;phase=next;done=Math.max(done,count);onProgress?.(Object.freeze({phase:name,completed:done,total}))};const stopped=new Promise((resolve,reject)=>{stop=()=>reject(failure())});signal.addEventListener("abort",stop,{once:true});try{if(signal.aborted)throw failure();const diagnostic=value=>{if(signal.aborted||finished)return;if(!value||typeof value.code!=="string"||!value.code||value.code.length>256||
41
+ [value.columnId,value.rowId].some(id=>id!==undefined&&(typeof id!=="string"||id.length>1024)))
42
+ throw new Error("CanopyX: invalid export diagnostic.");const item=Object.freeze({code:value.code,...(value.columnId===undefined?{}:{columnId:value.columnId}),...(value.rowId===undefined?{}:{rowId:value.rowId})});if(diagnostics.some(known=>known.code===item.code&&known.columnId===item.columnId&&known.rowId===item.rowId))return;if(diagnostics.length>=1000)throw new Error("CanopyX: too many export diagnostics.");diagnostics.push(item);options.onDiagnostic?.(item)};const counted=Object.freeze({request:capture.request,rows:async function*(more){read=0;for await(const row of capture.rows({signal:AbortSignal.any([signal,more?.signal].filter(Boolean))})){if(++read%100===0)report(inputsComplete?"generating":"reading",read);yield row}
43
+ inputsComplete=true;report("generating",read)}});const base=capture.request.fileName||grid.view.id.replace(/[^a-z0-9_-]/gi, "_");
44
+ report("reading",0);let blob=await Promise.race([Promise.resolve().then(()=>format.write(counted,Object.freeze({signal,fileName:base,format:format.id,includeMetadata:options.includeMetadata===true,diagnostic,progress:completed=>report(inputsComplete?"generating":"reading",completed)}))),stopped]).catch(error=>{throw error instanceof Error||error instanceof DOMException?error:new Error("CanopyX: export format '"+id+"' failed: "+String(error))});if(signal.aborted)throw failure();if(blob instanceof ArrayBuffer||ArrayBuffer.isView(blob))blob=new Blob([blob],{type:format.mime});if(!(blob instanceof Blob))throw new Error("CanopyX: export format '"+id+"' must write a Blob.");const fileName=blob.name||base+"."+format.extension;if(options.download!==false)saveExport(blob,fileName);report("generating",read);report("ready",read);return Object.freeze({blob,fileName,format:format.id,diagnostics:Object.freeze(diagnostics.slice())})}finally{finished=true;clearTimeout(clock);signal.removeEventListener("abort",stop)}},async*treeRows(grid,request,rows,signal){const fmt=formatting(request.locale,request.timeZone==="local",grid.labels);const columns=request.columns.map(column=>[column.id,grid.columns.findIndex(item=>item.id===column.id)]);for(const{id,level,path,node}of rows){if(grid.destroyed||signal.aborted)throw new DOMException("Grid cancelled.","AbortError");const cells=Object.create(null);for(const[key,index]of columns){const column=grid.columns[index],cell=grid.cellOf(node,column,index),tone=isCellObject(cell)&&cell.tone;cells[key]=Object.freeze({value:cellValue(cell)??null,text:cellText(cell,column,fmt),...(tone?{tone}:{})})}
7
45
  yield Object.freeze({id,level,path:Object.freeze(path),cells:Object.freeze(cells)})}}});function gridWriteClipboard(grid,text,signal){return grid.runGridHook(()=>{const clipboard=global.navigator.clipboard;if(typeof clipboard?.writeText!=="function")throw new Error("CanopyX: clipboard unavailable.");return clipboard.writeText(text)},signal)}
8
46
  Object.assign(EXPORT_IMPL,{async*exportRows(request,signal){const{scope}=request;const fmt=formatting(request.locale||this.lang,request.timeZone==="local",this.labels);gridOperationActive(this,signal);const remaining=new Set(request.selection),seen=new Set();if(scope==="selected"&&!remaining.size)return;const expected={revision:request.revision,total:request.total,matched:scope==="filtered"?request.matched:this.cursor?undefined:request.total};const tokens=new Set();let cursor,offset=0;while(this.cursor||offset<expected.matched){gridOperationActive(this,signal);const query=gridQuery(this.view,{...request.query,offset:this.cursor?undefined:offset,cursor});const page=(this.cursor?validateGridCursorPage:validateGridPage)(await this.requestPage(query,signal),this.view,query,expected);if(this.cursor&&(page.offset!==offset||scope!=="filtered"&&page.total!==null&&
9
47
  (page.matched!==null&&page.matched!==page.total||page.nextCursor===undefined&&page.offset+page.items.length!==page.total)))
10
- throw new Error("CanopyX: incomplete cursor export.");expected.matched=page.matched;for(const row of page.items){gridOperationActive(this,signal);if(seen.has(row.id))throw new Error("CanopyX: repeated export row ID.");seen.add(row.id);if(scope!=="selected"||remaining.delete(row.id)){const record=GRID_REPORTING.exportRecord?GRID_REPORTING.exportRecord(this,request,row):gridFreeze({id:row.id,cells:Object.fromEntries(request.columns.map(column=>[column.id,{value:cellValue(row.cells[column.id])??null,text:cellText(row.cells[column.id],column,fmt)}]))});yield GRID_REPORTING.formatRecord?GRID_REPORTING.formatRecord(this,row,record):record}}
48
+ throw new Error("CanopyX: incomplete cursor export.");expected.matched=page.matched;for(const row of page.items){gridOperationActive(this,signal);if(seen.has(row.id))throw new Error("CanopyX: repeated export row ID.");seen.add(row.id);if(scope!=="selected"||remaining.delete(row.id)){const record=GRID_REPORTING.exportRecord?GRID_REPORTING.exportRecord(this,request,row):gridFreeze({id:row.id,cells:Object.fromEntries(request.columns.map(column=>[column.id,{value:cellValue(row.cells[column.id])??null,text:cellText(row.cells[column.id],column,fmt)}]))});yield GRID_REPORTING.formatRecord?GRID_REPORTING.formatRecord(this,row,record,request):record}}
11
49
  offset +=this.cursor?page.items.length:request.query.limit;if(this.cursor){cursor=page.nextCursor;if(cursor===undefined)break;if(tokens.has(cursor))throw new Error("CanopyX: cursor export repeated a token.");tokens.add(cursor)}}
12
50
  gridOperationActive(this,signal);if(remaining.size)throw new Error("CanopyX: selected IDs absent from revision.")},async exportText(exported,maxRows=50000,delimiter,signal){if(!Number.isInteger(maxRows)||maxRows<1||maxRows>1000000)throw new Error("CanopyX: export row limit is 1 to 1000000.");const request=exported.request;if(request.recordCount>maxRows)throw new Error("CanopyX: client row limit exceeded.");const lines=[request.columns.map(column=>gridExportField(column.title,delimiter)).join(delimiter)];let count=0;for await(const row of exported.rows()){if(++count>maxRows)throw new Error("CanopyX: client row limit exceeded.");lines.push(request.columns.map(column=>{const value=request.values==="raw"?row.cells[column.id]?.value:row.cells[column.id]?.text;return gridExportField(row.level>1&&column.id===this.columns[0].id?" ".repeat(row.level-1)+(value??""):value,delimiter)}).join(delimiter))}
13
51
  gridOperationActive(this,signal);return lines.join("\r\n")+ "\r\n"},toCsv(capture,options){return this.exportText(capture,options.maxRows,",",options.signal)},async copy(capture,options){const text=await this.exportText(capture,options.maxRows===undefined?10000:options.maxRows,"\t",options.signal);await gridWriteClipboard(this,text,options.signal);return text},copyFocused(row,cell){if(this.selected.size)return this.copy({scope:"selected"});const columns=this.visibleColumns(),element=this.rendered.get(row),index=cell&&element?this.rowCells(element).indexOf(cell):-1;const text=index>=0?gridExportField(cellText(row.h.cells[columns[index].id],columns[index],this.fmt),"\t"):
14
52
  columns.map(column=>gridExportField(cellText(row.h.cells[column.id],column,this.fmt),"\t")).join("\t");return gridWriteClipboard(this,text)},exportToHost(capture,format,options){if(typeof format!=="string"||!format.trim()||typeof this.opts.onExport!=="function")throw new Error("CanopyX: invalid host export.");return this.runGridHook(signal=>this.opts.onExport({format,request:capture.request,capture,signal}),options.signal,exportTimeout(this,options))}});Object.assign(EXPORT_IMPL,{exportFromMenu(action){const{details,panel}=this.exportControl;this.renderGridExport(panel);details.open=true;if(!this.phone&&this.overflow)this.overflow.details.open=false;panel.cxRun(action)}});const exportMenu=function(panel){if(panel.cxRun)return panel.cxFormats();panel.replaceChildren();const reporting=!!this.view.reporting,scope=h("select"),labels=this.labels;const menu=this.opts.exportMenu??{};if(!menu||typeof menu!=="object"||Array.isArray(menu)||
15
- Object.keys(menu).some(key=>!["csv","copy","values"].includes(key))||
16
- ["csv","copy"].some(key=>menu[key]!==undefined&&typeof menu[key]!=="boolean")||
53
+ Object.keys(menu).some(key=>!["csv","copy","print","values"].includes(key))||
54
+ ["csv","copy","print"].some(key=>menu[key]!==undefined&&typeof menu[key]!=="boolean")||
17
55
  menu.values!==undefined&&!["raw","display"].includes(menu.values))
18
- throw new Error("CanopyX: exportMenu supports csv/copy booleans and raw/display values.");scope.setAttribute("aria-label",labels.gridExport);for(const[value,label]of this.exportScopes||[["filtered","gridFilteredResults"],["selected","gridSelectedRows"],["all","gridAllResults"]])
19
- scope.appendChild(gridOption(value,labels[label]));const status=h("p","cx-grid-delivery-status");status.setAttribute("role","status");const meter=h("progress");meter.setAttribute("aria-label",labels.gridExport);meter.hidden=true;let operation=null,formats=[];panel.cxExportBusy=()=>operation!==null;const cancel=gridButton(labels.gridCancelExport,()=>operation?.abort());cancel.hidden=true;const progress=value=>{if(this.destroyed||!operation||operation.signal.aborted)return;setText(status,fill(value.total===null?labels.gridExportReading:labels.gridExportProgress,{completed:numberFormat(this.lang).format(value.completed),total:value.total===null?"":numberFormat(this.lang).format(value.total)}));if(value.total===null)meter.removeAttribute("value");else{meter.max=Math.max(1,value.total);meter.value=value.completed}};const canExport=()=>this.view.type!=="grid"||this.defaultExportColumns().length>0;const busy=state=>{scope.disabled=state;cancel.hidden=meter.hidden=!state;for(const button of[csv,...formats,copy,this.printButton])if(button)button.disabled=state||!canExport()};const traced=(options,onProgress)=>reporting?{...options,onProgress}:options;panel.cxRun=action=>{if(operation)return;const controller=operation=new AbortController();busy(true);meter.removeAttribute("value");setText(status,labels.loading);Promise.resolve().then(()=>action({scope:scope.value,signal:controller.signal,...(menu.values?{values:menu.values}:{})},progress)).then(()=>{if(!this.destroyed)setText(status,"")},error=>{if(!this.destroyed)setText(status,error.name==="AbortError"?labels.gridExportCancelled:error.message);if(error.name!=="AbortError")this.emit("error",{error})}).finally(()=>{if(operation===controller)operation=null;if(!this.destroyed){const returnFocus=document.activeElement===cancel;busy(false);if(returnFocus)scope.focus()}})};const csv=gridButton(labels.gridDownloadCsv,()=>panel.cxRun(async(options,onProgress)=>{const exported=this.prepareExport(traced(options,onProgress));const text=await this.exportText(exported,50000,",",options.signal);gridOperationActive(this,options.signal);saveExport(new Blob([text],{type:"text/csv;charset=utf-8"}),(exported.request.fileName||this.view.id.replace(/[^a-z0-9_-]/gi, "_")) + ".csv");
20
- }));const copy=this.copy?gridButton(labels.gridCopy,()=>panel.cxRun((options,onProgress)=>this.copy(traced(options,onProgress)))):h("span");csv.hidden=menu.csv===false;copy.hidden=menu.copy===false;panel.cxFormats=()=>{if(operation)return;formats.forEach(button=>button.remove());try{formats=EXPORT_IMPL.formatsOf(this).filter(format=>format.showInMenu).map(format=>Object.assign(gridButton(format.label,()=>
21
- panel.cxRun((options,onProgress)=>this.exportAs(format.id,{...options,onProgress}))),{value:format.id}))}catch(error){formats=[];this.emit("error",{error})}
22
- copy.before(...formats);busy(false);if(!canExport())setText(status,labels.gridNoExportColumns);else if(status.textContent===labels.gridNoExportColumns)setText(status,"")};panel.append(scope,csv,copy,cancel,meter,status);panel.cxFormats()};DataGrid.prototype.renderGridExport=exportMenu;installed.add(context.EXPORT_IMPL)}};(global.CanopyXParts||=[]).push(["export",part])})(typeof window!=="undefined"?window:globalThis);
56
+ throw new Error("CanopyX: exportMenu supports csv/copy/print booleans and raw/display values.");scope.setAttribute("aria-label",labels.gridExport+": "+labels.gridRows);for(const[value,label]of this.exportScopes||[["filtered","gridFilteredResults"],["selected","gridSelectedRows"],["all","gridAllResults"]])
57
+ scope.appendChild(gridOption(value,labels[label]));panel.classList.add("cx-export-panel");const count=h("p","cx-export-count"),list=h("div","cx-export-formats"),actions=h("div","cx-export-actions");count.setAttribute("role","status");const updateCount=()=>{try{const request=this.prepareExport({scope:scope.value}).request;setText(count,request.recordCount===null?labels.gridUnknownCount:
58
+ numberFormat(this.lang).format(request.recordCount)+ " "+labels.gridRows)}catch{setText(count,scope.selectedOptions[0].textContent)}};scope.addEventListener("change",updateCount);const decorate=(button,title,hint,icon)=>{button.classList.add("cx-export-choice");button.setAttribute("aria-label",title);const symbol=h("span","cx-export-icon");symbol.setAttribute("aria-hidden","true");setText(symbol,icon);const content=h("span","cx-export-choice-text"),name=h("span","cx-export-choice-title");setText(name,title);content.append(name);if(hint){const note=h("span","cx-export-hint");setText(note,hint);content.append(note)}
59
+ button.replaceChildren(symbol,content);return button};const status=h("p","cx-grid-delivery-status");status.setAttribute("role","status");const meter=h("progress");meter.setAttribute("aria-label",labels.gridExport);meter.hidden=true;let operation=null,formats=[];panel.cxExportBusy=()=>operation!==null;const cancel=gridButton(labels.gridCancelExport,()=>operation?.abort());cancel.hidden=true;const progress=value=>{if(this.destroyed||!operation||operation.signal.aborted)return;setText(status,fill(value.total===null?labels.gridExportReading:labels.gridExportProgress,{completed:numberFormat(this.lang).format(value.completed),total:value.total===null?"":numberFormat(this.lang).format(value.total)}));if(value.total===null)meter.removeAttribute("value");else{meter.max=Math.max(1,value.total);meter.value=value.completed}};const canExport=()=>this.view.type!=="grid"||this.defaultExportColumns().length>0;const busy=state=>{scope.disabled=state;cancel.hidden=meter.hidden=!state;for(const button of[csv,...formats,copy,print,this.printButton])if(button)button.disabled=state||!canExport()};const traced=(options,onProgress)=>reporting?{...options,onProgress}:options;panel.cxRun=action=>{if(operation)return;const controller=operation=new AbortController();busy(true);meter.removeAttribute("value");setText(status,labels.loading);Promise.resolve().then(()=>action({scope:scope.value,signal:controller.signal,...(menu.values?{values:menu.values}:{})},progress)).then(()=>{if(!this.destroyed)setText(status,"")},error=>{if(!this.destroyed)setText(status,error.name==="AbortError"?labels.gridExportCancelled:error.message);if(error.name!=="AbortError")this.emit("error",{error})}).finally(()=>{if(operation===controller)operation=null;if(!this.destroyed){const returnFocus=document.activeElement===cancel;busy(false);if(returnFocus)scope.focus()}})};const csv=gridButton(labels.gridDownloadCsv,()=>panel.cxRun(async(options,onProgress)=>{const exported=this.prepareExport(traced(options,onProgress));const text=await this.exportText(exported,50000,",",options.signal);gridOperationActive(this,options.signal);saveExport(new Blob([text],{type:"text/csv;charset=utf-8"}),(exported.request.fileName||this.view.id.replace(/[^a-z0-9_-]/gi, "_")) + ".csv");
60
+ }));decorate(csv,labels.gridDownloadCsv,labels.gridExportCsvHint,"CSV");const copy=this.copy?decorate(gridButton(labels.gridCopy,()=>panel.cxRun((options,onProgress)=>this.copy(traced(options,onProgress)))),labels.gridExportCopy,null,"⧉"):h("span");const print=typeof this.print==="function"?gridButton(labels.gridPrint,()=>panel.cxRun((options,onProgress)=>
61
+ this.print({...options,onProgress}))):h("span");decorate(print,labels.gridPrint,null,"▤");print.hidden=true;this.exportPrintButton=print;csv.hidden=menu.csv===false;copy.hidden=!this.copy||menu.copy===false;panel.cxFormats=()=>{if(operation)return;formats.forEach(button=>button.remove());try{const available=EXPORT_IMPL.formatsOf(this);print.hidden=!(typeof this.print==="function"&&(menu.print??true));formats=available.filter(format=>format.showInMenu&&(!this.pivotState?.active||!["xlsx","pdf"].includes(format.id))).map(format=>Object.assign(decorate(gridButton(format.label,()=>
62
+ panel.cxRun((options,onProgress)=>this.exportAs(format.id,{...options,onProgress}))),format.label,format.id==="xlsx"?labels.gridExportWorkbookHint:format.id==="pdf"?labels.gridExportPdfHint:null,format.extension.toUpperCase()),{value:format.id}))}catch(error){formats=[];this.emit("error",{error})}
63
+ list.replaceChildren(...formats,csv);actions.hidden=copy.hidden&&print.hidden;updateCount();busy(false);if(!canExport())setText(status,labels.gridNoExportColumns);else if(status.textContent===labels.gridNoExportColumns)setText(status,"")};actions.append(copy,print);panel.append(scope,count,meter,status,cancel,list,actions);panel.cxFormats()};DataGrid.prototype.renderGridExport=exportMenu;installed.add(context.EXPORT_IMPL)}};(global.CanopyXParts||=[]).push(["export",part])})(typeof window!=="undefined"?window:globalThis);