@evotecit/canopyx 0.3.11 → 0.3.15

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 (102) hide show
  1. package/CanopyX.Html/Assets/canopyx-calendar-edit.js +36 -18
  2. package/CanopyX.Html/Assets/canopyx-calendar-ics.js +7 -5
  3. package/CanopyX.Html/Assets/canopyx-calendar.css +124 -116
  4. package/CanopyX.Html/Assets/canopyx-calendar.js +74 -54
  5. package/CanopyX.Html/Assets/canopyx-cell-range.css +8 -8
  6. package/CanopyX.Html/Assets/canopyx-cell-range.js +1 -1
  7. package/CanopyX.Html/Assets/canopyx-changes.css +113 -113
  8. package/CanopyX.Html/Assets/canopyx-changes.js +7 -10
  9. package/CanopyX.Html/Assets/canopyx-column-groups.css +5 -5
  10. package/CanopyX.Html/Assets/canopyx-column-groups.js +1 -1
  11. package/CanopyX.Html/Assets/canopyx-data.js +2 -2
  12. package/CanopyX.Html/Assets/canopyx-detail-rows.css +9 -9
  13. package/CanopyX.Html/Assets/canopyx-detail-rows.js +2 -2
  14. package/CanopyX.Html/Assets/canopyx-editing.css +18 -18
  15. package/CanopyX.Html/Assets/canopyx-editing.js +1 -1
  16. package/CanopyX.Html/Assets/canopyx-enhance.js +2 -2
  17. package/CanopyX.Html/Assets/canopyx-export.js +8 -4
  18. package/CanopyX.Html/Assets/canopyx-facets.css +33 -33
  19. package/CanopyX.Html/Assets/canopyx-facets.js +10 -11
  20. package/CanopyX.Html/Assets/canopyx-filter-editor.css +57 -57
  21. package/CanopyX.Html/Assets/canopyx-filter-editor.js +15 -11
  22. package/CanopyX.Html/Assets/canopyx-flow-checks.css +11 -11
  23. package/CanopyX.Html/Assets/canopyx-flow-checks.js +1 -1
  24. package/CanopyX.Html/Assets/canopyx-flow-command.css +7 -7
  25. package/CanopyX.Html/Assets/canopyx-flow-command.js +1 -1
  26. package/CanopyX.Html/Assets/canopyx-flow-dialog.css +7 -7
  27. package/CanopyX.Html/Assets/canopyx-flow-dialog.js +1 -1
  28. package/CanopyX.Html/Assets/canopyx-flow-drafts.css +7 -7
  29. package/CanopyX.Html/Assets/canopyx-flow-drafts.js +1 -1
  30. package/CanopyX.Html/Assets/canopyx-flow-entry.css +17 -76
  31. package/CanopyX.Html/Assets/canopyx-flow-entry.js +9 -3
  32. package/CanopyX.Html/Assets/canopyx-flow-host.css +4 -4
  33. package/CanopyX.Html/Assets/canopyx-flow-host.js +1 -1
  34. package/CanopyX.Html/Assets/canopyx-flow-module.mjs +9 -3
  35. package/CanopyX.Html/Assets/canopyx-flow-pick.css +4 -4
  36. package/CanopyX.Html/Assets/canopyx-flow-pick.js +1 -1
  37. package/CanopyX.Html/Assets/canopyx-flow-preview.css +6 -6
  38. package/CanopyX.Html/Assets/canopyx-flow-preview.js +1 -1
  39. package/CanopyX.Html/Assets/canopyx-flow-records.css +3 -3
  40. package/CanopyX.Html/Assets/canopyx-flow-records.js +1 -1
  41. package/CanopyX.Html/Assets/canopyx-flow-tasks.css +17 -17
  42. package/CanopyX.Html/Assets/canopyx-flow-tasks.js +1 -1
  43. package/CanopyX.Html/Assets/canopyx-flow.css +79 -79
  44. package/CanopyX.Html/Assets/canopyx-flow.js +1 -1
  45. package/CanopyX.Html/Assets/canopyx-format-editor.css +23 -23
  46. package/CanopyX.Html/Assets/canopyx-format-editor.js +2 -3
  47. package/CanopyX.Html/Assets/canopyx-formatting.css +17 -17
  48. package/CanopyX.Html/Assets/canopyx-formatting.js +5 -5
  49. package/CanopyX.Html/Assets/canopyx-forms.css +5 -5
  50. package/CanopyX.Html/Assets/canopyx-forms.js +1 -1
  51. package/CanopyX.Html/Assets/canopyx-grid-module.mjs +134 -77
  52. package/CanopyX.Html/Assets/canopyx-grid-options.css +10 -0
  53. package/CanopyX.Html/Assets/canopyx-grid-options.js +47 -0
  54. package/CanopyX.Html/Assets/canopyx-grid.css +199 -261
  55. package/CanopyX.Html/Assets/canopyx-grid.js +134 -77
  56. package/CanopyX.Html/Assets/canopyx-grouping.css +17 -17
  57. package/CanopyX.Html/Assets/canopyx-grouping.js +12 -9
  58. package/CanopyX.Html/Assets/canopyx-highlight.css +14 -14
  59. package/CanopyX.Html/Assets/canopyx-highlight.js +2 -2
  60. package/CanopyX.Html/Assets/canopyx-interactive.css +94 -155
  61. package/CanopyX.Html/Assets/canopyx-interactive.js +58 -24
  62. package/CanopyX.Html/Assets/canopyx-labels-de.js +5 -0
  63. package/CanopyX.Html/Assets/canopyx-labels-es.js +5 -0
  64. package/CanopyX.Html/Assets/canopyx-labels-fr.js +5 -0
  65. package/CanopyX.Html/Assets/canopyx-labels-it.js +5 -0
  66. package/CanopyX.Html/Assets/canopyx-labels-pl.js +5 -0
  67. package/CanopyX.Html/Assets/canopyx-layouts.css +17 -17
  68. package/CanopyX.Html/Assets/canopyx-layouts.js +4 -12
  69. package/CanopyX.Html/Assets/canopyx-link.css +7 -7
  70. package/CanopyX.Html/Assets/canopyx-link.js +1 -1
  71. package/CanopyX.Html/Assets/canopyx-live.css +9 -9
  72. package/CanopyX.Html/Assets/canopyx-live.js +2 -2
  73. package/CanopyX.Html/Assets/canopyx-matrix.css +74 -74
  74. package/CanopyX.Html/Assets/canopyx-matrix.js +3 -4
  75. package/CanopyX.Html/Assets/canopyx-module.mjs +58 -24
  76. package/CanopyX.Html/Assets/canopyx-pager.css +2 -2
  77. package/CanopyX.Html/Assets/canopyx-pager.js +1 -1
  78. package/CanopyX.Html/Assets/canopyx-presenters.js +11 -0
  79. package/CanopyX.Html/Assets/canopyx-record-panel.css +49 -49
  80. package/CanopyX.Html/Assets/canopyx-record-panel.js +13 -11
  81. package/CanopyX.Html/Assets/canopyx-reporting-module.mjs +142 -84
  82. package/CanopyX.Html/Assets/canopyx-reporting.css +214 -276
  83. package/CanopyX.Html/Assets/canopyx-reporting.js +142 -84
  84. package/CanopyX.Html/Assets/canopyx-timeline.css +28 -28
  85. package/CanopyX.Html/Assets/canopyx-timeline.js +11 -10
  86. package/CanopyX.Html/Assets/canopyx-tree-tools.css +47 -46
  87. package/CanopyX.Html/Assets/canopyx-tree-tools.js +5 -4
  88. package/CanopyX.Html/Assets/canopyx.css +81 -142
  89. package/CanopyX.Html/Assets/canopyx.js +58 -24
  90. package/JavaScript/calendar.d.ts +15 -0
  91. package/JavaScript/grid.d.ts +29 -4
  92. package/JavaScript/hosts.d.ts +11 -0
  93. package/JavaScript/locales/de.mjs +5 -0
  94. package/JavaScript/locales/es.mjs +5 -0
  95. package/JavaScript/locales/fr.mjs +5 -0
  96. package/JavaScript/locales/it.mjs +5 -0
  97. package/JavaScript/locales/pl.mjs +5 -0
  98. package/JavaScript/plugins.d.ts +4 -0
  99. package/JavaScript/protocol.d.ts +14 -1
  100. package/JavaScript/records.d.ts +2 -0
  101. package/README.md +6 -2
  102. package/package.json +7 -1
@@ -0,0 +1,10 @@
1
+ .cx-data-grid .cx-footers{position:sticky;bottom:0;--cx-footer-layer:3;z-index:var(--cx-footer-layer);width:max-content;min-width:100%;background:var(--hfx-color-surface,light-dark(#ffffff,#16181c));border-top:var(--cx-size-2px) solid var(--hfx-color-border,light-dark(#e2e4e7,#272a30))}
2
+ .cx-data-grid .cx-footer-row{display:flex;position:relative;padding-top:var(--cx-size-22px);min-height:var(--cx-row-h);background:color-mix(in srgb,var(--hfx-color-accent,light-dark(#11776f,#37c9b8)) 5%,var(--hfx-color-surface,light-dark(#ffffff,#16181c)))}
3
+ .cx-data-grid .cx-footer-row+.cx-footer-row{border-top:var(--cx-size-1px) solid var(--hfx-color-border,light-dark(#e2e4e7,#272a30))}
4
+ .cx-data-grid .cx-footer-fill{flex:1;min-width:0;border:0}
5
+ .cx-data-grid .cx-footer-kind{color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));font-weight:normal}
6
+ .cx-data-grid .cx-footer-cell strong{font-variant-numeric:tabular-nums}
7
+ .cx-data-grid .cx-footer-label{position:absolute;inset-block-start:var(--cx-size-3px);inset-inline-start:var(--cx-size-10px);color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));font-size:var(--cx-size-11px);font-weight:var(--hfx-table-header-weight,var(--hfx-weight-semibold,600))}
8
+ .cx-data-grid .cx-footer-cell{display:block;position:relative;flex-shrink:0;box-sizing:border-box;min-height:var(--cx-row-h);height:auto;padding:var(--cx-size-7px) var(--cx-size-10px);white-space:normal;overflow-wrap:anywhere;font-weight:var(--hfx-table-header-weight,var(--hfx-weight-semibold,600));border-inline-end:var(--cx-size-1px) solid var(--hfx-color-border,light-dark(#e2e4e7,#272a30))}
9
+ .cx-data-grid .cx-footer-cell[data-cx-pin]{position:sticky;background:var(--hfx-color-surface,light-dark(#ffffff,#16181c))}
10
+ .cx-data-grid .cx-footer-hidden{display:block;margin-block-start:var(--cx-size-4px);font-weight:normal;color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));text-align:start}
@@ -0,0 +1,47 @@
1
+ (function(global){"use strict";const installed=new WeakSet();const part={version:"e74170bbb841519c",install(context){if(installed.has(context.GRID_OPTIONS))return;const{DataGrid,GRID_OPTIONS,GRID_OPTION_GRIDS,h,setText,fill,gridQuery,gridQueryRows,gridCompare,gridKeyCompare,gridSortScalar,gridMatches,gridMatchesGroup,gridScalar,gridFreeze,gridOperationActive,numberText,isCellObject,cellValue,requireIdentifier,validStructuralCount,PROTOCOL,gridResultShape,GRID_REPORTING}=context;const GROUP_OPERATIONS=["count","sum","average","minimum","maximum","distinctCount"];const reportingGroupKey=value=>JSON.stringify([typeof value,value]);const groupCounting=operation=>operation==="count"||operation==="distinctCount";function groupSummaryAggregates(view){const bindings=new Map();const add=aggregate=>{const existing=bindings.get(aggregate.id);if(existing&&(existing.column!==aggregate.column||existing.operation!==aggregate.operation))
2
+ throw new Error("CanopyX: an aggregate identity must use the same column and operation.");if(!existing)bindings.set(aggregate.id,aggregate)};for(const aggregate of view.reporting?.aggregates||[])add(aggregate);for(const footer of view.footers||[])if(footer.scope==="all"||view.presentation!=="paged")
3
+ footer.aggregates.forEach(add);if(bindings.size>32)throw new Error("CanopyX: at most 32 full-query aggregates are supported.");return[...bindings.values()]}
4
+ function groupCustomAggregates(grid){const found=new Map();for(const plugin of grid.plugins)for(const[name,entry]of Object.entries(plugin.aggregates||{})){const definition=typeof entry==="function"?{compute:entry}:entry;if(!/^[a-z][a-zA-Z\d]{0,31}$/.test(name) || GROUP_OPERATIONS.includes(name) || !definition || typeof definition.compute !== "function" ||
5
+ definition.label!==undefined&&(typeof definition.label!=="string"||!definition.label.includes("{value}")||definition.label.length>64))
6
+ throw new Error("CanopyX: plugin '"+plugin.name+"' registers an invalid aggregate '"+name+"'.");found.set(name,definition)}
7
+ return found}
8
+ function groupAggregateValue(grid,aggregate,rows,first,last){const column=grid.view.columns.find(item=>item.id===aggregate.column),operation=aggregate.operation;if(!GROUP_OPERATIONS.includes(operation)){const values=[];for(let i=first;i<last;i++){const value=gridScalar(rows[i].cells[column.id],column);if(value!==null)values.push(value)}
9
+ const value=grid.groupAggregates.get(operation).compute(Object.freeze(values),Object.freeze({column:column.id,aggregate:aggregate.id}));if(value!==null&&(typeof value!=="number"||!Number.isFinite(value)||Math.abs(value)>Number.MAX_SAFE_INTEGER))
10
+ throw new Error("CanopyX: custom aggregate '"+operation+"' returned a value that is not a finite number or null.");return value}
11
+ let count=0,sum=0,correction=0,minimum=Infinity,maximum=-Infinity;const distinct=operation==="distinctCount"?new Set():null;for(let i=first;i<last;i++){const value=gridScalar(rows[i].cells[column.id],column);if(value===null)continue;count++;if(distinct)distinct.add(reportingGroupKey(value));else if(operation!=="count"){const total=sum+value;correction +=Math.abs(sum)>=Math.abs(value)?sum-total+value:value-total+sum;sum=total;minimum=Math.min(minimum,value);maximum=Math.max(maximum,value)}}
12
+ return operation==="count"?count:distinct?distinct.size:!count?null:
13
+ operation==="sum"?sum+correction:operation==="average"?(sum+correction)/ count : operation === "minimum" ? minimum : maximum;
14
+ }
15
+ GRID_OPTIONS.attach=grid=>{if(GRID_OPTION_GRIDS.has(grid.view))return;const filters=Object.assign(Object.create(null),...grid.plugins.map(plugin=>plugin.filterOperators));const comparators=Object.assign(Object.create(null),...grid.plugins.map(plugin=>plugin.sortComparators));for(const name of[...Object.keys(filters),...Object.keys(comparators)])
16
+ if(!/^[a-z][a-zA-Z\d]{0,31}$/.test(name)) throw new Error("CanopyX: invalid query hook name.");
17
+ for(const name of Object.keys(filters))if(["eq","ne","contains","startsWith","in","gt","ge","lt","le","isNull","isNotNull"].includes(name))
18
+ throw new Error("CanopyX: custom filters cannot replace a built-in operator.");for(const column of grid.view.columns)if(grid.view.source.kind==="inline"&&column.sortComparator&&!comparators[column.sortComparator])
19
+ throw new Error("CanopyX: no plugin provides sort comparator '"+column.sortComparator+"'.");grid.groupAggregates=groupCustomAggregates(grid);validateFooters(grid);GRID_OPTION_GRIDS.set(grid.view,{grid,filters,comparators});grid.lifetime.signal.addEventListener("abort",()=>GRID_OPTION_GRIDS.delete(grid.view),{once:true})};GRID_OPTIONS.queryRows=(view,query,columns,compare)=>{const{grid,filters}=GRID_OPTION_GRIDS.get(view);const context=column=>Object.freeze({column:column.id,signal:grid.dataLifetime.signal});const matches=(row,filter,column)=>{if(["eq","ne","contains","startsWith","in","gt","ge","lt","le","isNull","isNotNull"].includes(filter.operator))
20
+ return gridMatches(row,filter,column);const hook=filters[filter.operator];if(!hook)throw new Error("CanopyX: no plugin provides filter operator '"+filter.operator+"'.");const result=hook(gridScalar(row.cells[column.id],column),Object.freeze(filter.values.slice()),context(column));if(typeof result!=="boolean")throw new Error("CanopyX: filter hooks return a boolean.");return result};return gridQueryRows(view,query,columns,compare===null?null:GRID_OPTIONS.compare(view,query,columns,compare),matches,true)};GRID_OPTIONS.compare=(view,query,columns,compare)=>{const{grid,comparators}=GRID_OPTION_GRIDS.get(view);const context=column=>Object.freeze({column:column.id,signal:grid.dataLifetime.signal});return(a,b,column,direction)=>{if(view.reporting?.grouping?.column===column.id||query.groupBy?.some(level=>level.column===column.id))return compare(a,b,column,direction);const fixed=view.preSort?.some(key=>key.column===column.id);for(const id of!fixed&&column.orderData?.length?column.orderData:[column.id]){const target=columns.get(id),hook=comparators[column.sortComparator];let result;if(hook){const left=gridSortScalar(a.cells[id],target),right=gridSortScalar(b.cells[id],target);if(left===null||right===null)result=left===right?0:left===null?1:-1;else{result=hook(left,right,context(target));if(!Number.isFinite(result))throw new Error("CanopyX: sort hooks return a finite number.");if(direction==="desc")result=-result}}else result=compare(a,b,target,direction);if(result)return result}
21
+ return 0}};const FOOTER_PAGE=Symbol("footer-page");function validateFooters(grid){const footers=grid.view.footers||[],ids=new Set();if(!Array.isArray(footers)||footers.length>16||footers.length&&grid.cursor)
22
+ throw new Error("CanopyX: footers need offset navigation and at most 16 rows.");for(const footer of footers){requireIdentifier(footer?.id,"footer ID");if(ids.has(footer.id)||typeof footer.label!=="string"||!footer.label.trim()||footer.label.length>512||
23
+ !["page","all"].includes(footer.scope)||!Array.isArray(footer.aggregates)||!footer.aggregates.length||footer.aggregates.length>32)
24
+ throw new Error("CanopyX: invalid footer row.");ids.add(footer.id);const aggregates=new Set();for(const aggregate of footer.aggregates){requireIdentifier(aggregate?.id,"footer aggregate ID");const column=grid.view.columns.find(column=>column.id===aggregate.column),custom=!GROUP_OPERATIONS.includes(aggregate.operation);if(!column||aggregates.has(aggregate.id)||typeof aggregate.label!=="string"||!aggregate.label.trim()||aggregate.label.length>512||
25
+ typeof aggregate.operation!=="string"||!/^[a-z][a-zA-Z\d]{0,31}$/.test(aggregate.operation) ||
26
+ ["sum","average","minimum","maximum"].includes(aggregate.operation)&&column.kind!=="number"||
27
+ custom&&grid.view.source.kind==="inline"&&!grid.groupAggregates.has(aggregate.operation))
28
+ throw new Error("CanopyX: invalid footer aggregate or missing custom aggregate plugin.");aggregates.add(aggregate.id)}}
29
+ groupSummaryAggregates(grid.view)}
30
+ GRID_OPTIONS.page=async(grid,query,signal,page)=>{if(!grid.view.footers?.length||signal!==grid.dataLifetime.signal)return page;const current=grid.pendingQuery||grid.acceptedQuery;if(!current||gridResultShape(current)!==gridResultShape(query))return page;const suppliedRows=page.footerValues;if(suppliedRows!==undefined){if(!suppliedRows||typeof suppliedRows!=="object"||Array.isArray(suppliedRows))throw new Error("CanopyX: invalid footer values.");for(const[id,entries]of Object.entries(suppliedRows)){const footer=grid.view.footers.find(footer=>footer.id===id);if(!footer||!entries||typeof entries!=="object"||Array.isArray(entries)||Object.keys(entries).some(key=>!footer.aggregates.some(aggregate=>aggregate.id===key)))
31
+ throw new Error("CanopyX: footer values require declared rows and aggregates.")}}
32
+ const values=Object.create(null),inline=grid.view.source.kind==="inline";let ordered;if(inline)ordered=query.groupBy?.length?GRID_OPTIONS.groupRows(grid.view,query):
33
+ gridQueryRows(grid.view,query,new Map(grid.view.columns.map(column=>[column.id,column])),GRID_REPORTING.compare?.(grid.view));for(const footer of grid.view.footers){const supplied=page.footerValues?.[footer.id],cells=values[footer.id]=Object.create(null);const count=footer.scope==="all"||!grid.paged?page.matched:Math.min(grid.pendingPaging?.pageLength||grid.pageLength,page.matched-query.offset);const rows=inline?footer.scope==="all"||!grid.paged?ordered:ordered.slice(query.offset,query.offset+count):
34
+ footer.scope==="page"&&count<=page.items.length?page.items.slice(0,count):null;for(const aggregate of footer.aggregates){const explicit=supplied&&Object.hasOwn(supplied,aggregate.id);const value=explicit?supplied[aggregate.id]:rows&&(GROUP_OPERATIONS.includes(aggregate.operation)||grid.groupAggregates.has(aggregate.operation))?
35
+ groupAggregateValue(grid,aggregate,rows,0,rows.length):undefined;if(value!==undefined&&value!==null&&(typeof value!=="number"||!Number.isFinite(value)||Math.abs(value)>Number.MAX_SAFE_INTEGER)||
36
+ groupCounting(aggregate.operation)&&value!==undefined&&(!validStructuralCount(value)||value>count)||
37
+ count===0&&!groupCounting(aggregate.operation)&&value!==undefined&&value!==null)
38
+ throw new Error("CanopyX: invalid footer aggregate value.");cells[aggregate.id]=value}}
39
+ gridOperationActive(grid,signal);return{...page,[FOOTER_PAGE]:{query,values}}};const FOOTER_STORE=DataGrid.prototype.storePage;DataGrid.prototype.storePage=function(page){const result=FOOTER_STORE.call(this,page),candidate=page[FOOTER_PAGE];if(candidate&&this.acceptedQuery&&page.revision===this.snapshot.revision&&
40
+ candidate.query.offset===this.acceptedQuery.offset&&gridResultShape(candidate.query)===gridResultShape(this.acceptedQuery))
41
+ this.footerCandidate={...candidate,page};return result};const FOOTER_RENDER=DataGrid.prototype.render;DataGrid.prototype.render=function(){FOOTER_RENDER.call(this);if(this.view.footers?.length&&this.acceptedQuery&&this.footerCandidate&&
42
+ this.footerCandidate.page.revision===this.snapshot.revision&&this.footerCandidate.query.offset===this.acceptedQuery.offset&&gridResultShape(this.footerCandidate.query)===gridResultShape(this.acceptedQuery))renderFooters(this)};function footerAggregateText(grid,footer,aggregate){const column=grid.view.columns.find(column=>column.id===aggregate.column),value=grid.footerCandidate.values[footer.id][aggregate.id];const format=aggregate.format||(groupCounting(aggregate.operation)?{minimumFractionDigits:0,maximumFractionDigits:0}:column.format);return aggregate.label+": " +(value===undefined?"Unavailable":value===null?"—":numberText(value,grid.lang,format))}
43
+ function renderFooters(grid){const key=JSON.stringify([grid.acceptedQuery,grid.footerCandidate.values,grid.cols.map(column=>[column.id,column.renderWidth??column.width,column.effectivePin,column.pinOffset])]);grid.headCells.forEach((cell,index)=>{cell.id||=grid.id+"-footer-column-"+index});const bodyCount=(grid.rowProjection?.rowCount()??grid.rowCount())+ 1 +(grid.headDepth||0)+(grid.summaryControls?.parentElement===grid.grid?1:0);grid.grid.setAttribute("aria-rowcount",String(bodyCount+grid.view.footers.length));if(grid.footerRoot)[...grid.footerRoot.children].forEach((row,index)=>row.setAttribute("aria-rowindex",String(bodyCount+index+1)));if(grid.footerKey===key)return;grid.footerKey=key;const root=grid.footerRoot||=grid.grid.appendChild(h("div","cx-footers"));root.setAttribute("role","rowgroup");root.setAttribute("aria-label","Footer totals");root.replaceChildren();for(const[footerIndex,footer]of grid.view.footers.entries()){const row=h("div","cx-footer-row");row.setAttribute("role","row");row.setAttribute("data-cx-footer",footer.id);row.setAttribute("aria-rowindex",String(bodyCount+footerIndex+1));row.style.width=grid.rowWidth+"px";row.style.minWidth="100%";const label=h("span","cx-footer-label");setText(label,footer.label);label.id=grid.id+"-footer-label-"+footerIndex;row.setAttribute("aria-label",footer.label);row.appendChild(label);for(const[columnIndex,column]of grid.cols.entries()){const cell=h("div","cx-cell cx-footer-cell" +(column.className?" "+column.className:""));cell.setAttribute("role","gridcell");cell.setAttribute("data-cx-column",column.id);cell.style.width=(column.renderWidth??column.width)+ "px";grid.applyColumnPin(cell,column);cell.style.textAlign=column.alignment||(column.kind==="number"?"end":"start");const parts=footer.aggregates.filter(aggregate=>aggregate.column===column.id).map(aggregate=>footerAggregateText(grid,footer,aggregate));const text=h("span");text.id=grid.id+"-footer-value-"+footerIndex+"-"+columnIndex;parts.forEach((part,index)=>{if(index)text.appendChild(document.createTextNode(" · "));const lead=h("span","cx-footer-kind"),value=h("strong"),at=part.indexOf(": ");setText(lead,part.slice(0,at+2));setText(value,part.slice(at+2));text.append(lead,value)});cell.appendChild(text);let extra;if(columnIndex===0){const hidden=footer.aggregates.filter(aggregate=>!grid.cols.some(column=>column.id===aggregate.column));if(hidden.length){extra=h("span","cx-footer-hidden");extra.id=text.id+"-hidden";setText(extra,hidden.map(aggregate=>grid.view.columns.find(column=>column.id===aggregate.column).title+" — "+footerAggregateText(grid,footer,aggregate)).join(" · "));cell.appendChild(extra)}}
44
+ cell.setAttribute("aria-colindex",String(columnIndex+1));cell.setAttribute("aria-labelledby",grid.headCells[columnIndex].id+" "+label.id+" "+text.id +(extra?" "+extra.id:""));cell.title=[footer.label,...parts].join(" — ");row.appendChild(cell)}
45
+ const filler=h("div","cx-footer-fill");filler.setAttribute("aria-hidden","true");row.appendChild(filler);root.appendChild(row)}
46
+ if(grid.summaryControls)grid.summaryControls.style.bottom=root.offsetHeight+"px"}
47
+ installed.add(context.GRID_OPTIONS)}};(global.CanopyXParts||=[]).push(["grid-options",part])})(typeof window!=="undefined"?window:globalThis);