@evotecit/canopyx 0.3.16 → 0.3.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/CanopyX.Html/Assets/canopyx-board.css +50 -0
  2. package/CanopyX.Html/Assets/canopyx-board.js +39 -0
  3. package/CanopyX.Html/Assets/canopyx-buckets.js +15 -0
  4. package/CanopyX.Html/Assets/canopyx-calculations.css +7 -0
  5. package/CanopyX.Html/Assets/canopyx-calculations.js +52 -0
  6. package/CanopyX.Html/Assets/canopyx-calendar-edit.js +3 -3
  7. package/CanopyX.Html/Assets/canopyx-calendar.css +5 -24
  8. package/CanopyX.Html/Assets/canopyx-calendar.js +14 -19
  9. package/CanopyX.Html/Assets/canopyx-cell-range.js +16 -14
  10. package/CanopyX.Html/Assets/canopyx-changes.css +6 -24
  11. package/CanopyX.Html/Assets/canopyx-changes.js +18 -15
  12. package/CanopyX.Html/Assets/canopyx-compare.css +37 -0
  13. package/CanopyX.Html/Assets/canopyx-compare.js +12 -0
  14. package/CanopyX.Html/Assets/canopyx-data.js +3 -3
  15. package/CanopyX.Html/Assets/canopyx-editing.css +2 -2
  16. package/CanopyX.Html/Assets/canopyx-editing.js +2 -2
  17. package/CanopyX.Html/Assets/canopyx-enhance.js +4 -4
  18. package/CanopyX.Html/Assets/canopyx-export.js +54 -13
  19. package/CanopyX.Html/Assets/canopyx-facets.css +14 -5
  20. package/CanopyX.Html/Assets/canopyx-facets.js +19 -15
  21. package/CanopyX.Html/Assets/canopyx-filter-editor.css +71 -85
  22. package/CanopyX.Html/Assets/canopyx-filter-editor.js +20 -16
  23. package/CanopyX.Html/Assets/canopyx-flow-checks.css +1 -1
  24. package/CanopyX.Html/Assets/canopyx-flow-checks.js +1 -1
  25. package/CanopyX.Html/Assets/canopyx-flow-dialog.css +2 -2
  26. package/CanopyX.Html/Assets/canopyx-flow-dialog.js +1 -1
  27. package/CanopyX.Html/Assets/canopyx-flow-entry.css +136 -12
  28. package/CanopyX.Html/Assets/canopyx-flow-entry.js +2 -2
  29. package/CanopyX.Html/Assets/canopyx-flow-module.mjs +2 -2
  30. package/CanopyX.Html/Assets/canopyx-format-editor.js +2 -2
  31. package/CanopyX.Html/Assets/canopyx-formatting.js +10 -9
  32. package/CanopyX.Html/Assets/canopyx-grid-module.mjs +182 -128
  33. package/CanopyX.Html/Assets/canopyx-grid.css +228 -96
  34. package/CanopyX.Html/Assets/canopyx-grid.js +182 -128
  35. package/CanopyX.Html/Assets/canopyx-grouping.css +22 -15
  36. package/CanopyX.Html/Assets/canopyx-grouping.js +24 -17
  37. package/CanopyX.Html/Assets/canopyx-interactive.css +197 -74
  38. package/CanopyX.Html/Assets/canopyx-interactive.js +38 -45
  39. package/CanopyX.Html/Assets/canopyx-labels-de.js +177 -9
  40. package/CanopyX.Html/Assets/canopyx-labels-es.js +177 -9
  41. package/CanopyX.Html/Assets/canopyx-labels-fr.js +176 -8
  42. package/CanopyX.Html/Assets/canopyx-labels-it.js +176 -8
  43. package/CanopyX.Html/Assets/canopyx-labels-pl.js +176 -8
  44. package/CanopyX.Html/Assets/canopyx-layouts.css +48 -20
  45. package/CanopyX.Html/Assets/canopyx-layouts.js +36 -14
  46. package/CanopyX.Html/Assets/canopyx-link.js +2 -2
  47. package/CanopyX.Html/Assets/canopyx-live.css +1 -3
  48. package/CanopyX.Html/Assets/canopyx-live.js +3 -3
  49. package/CanopyX.Html/Assets/canopyx-matrix.css +1 -8
  50. package/CanopyX.Html/Assets/canopyx-matrix.js +11 -11
  51. package/CanopyX.Html/Assets/canopyx-module.mjs +38 -45
  52. package/CanopyX.Html/Assets/canopyx-office-export.js +2936 -0
  53. package/CanopyX.Html/Assets/canopyx-path.css +45 -0
  54. package/CanopyX.Html/Assets/canopyx-path.js +28 -0
  55. package/CanopyX.Html/Assets/canopyx-pivot.css +75 -0
  56. package/CanopyX.Html/Assets/canopyx-pivot.js +72 -0
  57. package/CanopyX.Html/Assets/canopyx-plan.css +56 -0
  58. package/CanopyX.Html/Assets/canopyx-plan.js +67 -0
  59. package/CanopyX.Html/Assets/canopyx-presenters.js +2 -2
  60. package/CanopyX.Html/Assets/canopyx-record-panel.css +8 -7
  61. package/CanopyX.Html/Assets/canopyx-record-panel.js +15 -14
  62. package/CanopyX.Html/Assets/canopyx-reporting-module.mjs +222 -161
  63. package/CanopyX.Html/Assets/canopyx-reporting.css +240 -107
  64. package/CanopyX.Html/Assets/canopyx-reporting.js +222 -161
  65. package/CanopyX.Html/Assets/canopyx-stack.css +35 -0
  66. package/CanopyX.Html/Assets/canopyx-stack.js +24 -0
  67. package/CanopyX.Html/Assets/canopyx-time-slider.css +18 -0
  68. package/CanopyX.Html/Assets/canopyx-time-slider.js +27 -0
  69. package/CanopyX.Html/Assets/canopyx-timeline.js +6 -5
  70. package/CanopyX.Html/Assets/canopyx-tree-tools.css +36 -35
  71. package/CanopyX.Html/Assets/canopyx-tree-tools.js +15 -12
  72. package/CanopyX.Html/Assets/canopyx-ui-menu.css +41 -0
  73. package/CanopyX.Html/Assets/canopyx-ui-menu.js +64 -0
  74. package/CanopyX.Html/Assets/canopyx-ui-navigation.css +73 -0
  75. package/CanopyX.Html/Assets/canopyx-ui-navigation.js +39 -0
  76. package/CanopyX.Html/Assets/canopyx-ui-overlays.css +79 -0
  77. package/CanopyX.Html/Assets/canopyx-ui-overlays.js +42 -0
  78. package/CanopyX.Html/Assets/canopyx-ui-picker.css +12 -0
  79. package/CanopyX.Html/Assets/canopyx-ui-picker.js +6 -0
  80. package/CanopyX.Html/Assets/canopyx-ui-stepper.css +174 -0
  81. package/CanopyX.Html/Assets/canopyx-ui-stepper.js +48 -0
  82. package/CanopyX.Html/Assets/canopyx-ui-tabs.css +153 -0
  83. package/CanopyX.Html/Assets/canopyx-ui-tabs.js +47 -0
  84. package/CanopyX.Html/Assets/canopyx-values.css +5 -0
  85. package/CanopyX.Html/Assets/canopyx-values.js +13 -0
  86. package/CanopyX.Html/Assets/canopyx-view-intents.css +12 -0
  87. package/CanopyX.Html/Assets/canopyx-view-intents.js +56 -0
  88. package/CanopyX.Html/Assets/canopyx.css +197 -71
  89. package/CanopyX.Html/Assets/canopyx.js +29 -19
  90. package/JavaScript/board.d.ts +30 -0
  91. package/JavaScript/changes.d.ts +4 -1
  92. package/JavaScript/facets.d.ts +5 -3
  93. package/JavaScript/grid.d.ts +150 -16
  94. package/JavaScript/hosts.d.ts +14 -1
  95. package/JavaScript/index.d.ts +4 -2
  96. package/JavaScript/locales/de.mjs +177 -9
  97. package/JavaScript/locales/es.mjs +177 -9
  98. package/JavaScript/locales/fr.mjs +176 -8
  99. package/JavaScript/locales/it.mjs +176 -8
  100. package/JavaScript/locales/pl.mjs +176 -8
  101. package/JavaScript/matrix.d.ts +4 -1
  102. package/JavaScript/path.d.ts +33 -0
  103. package/JavaScript/pivot.d.ts +135 -0
  104. package/JavaScript/plan.d.ts +23 -0
  105. package/JavaScript/protocol.d.ts +16 -3
  106. package/JavaScript/records.d.ts +2 -0
  107. package/JavaScript/stack.d.ts +28 -0
  108. package/JavaScript/time-slider.d.ts +58 -0
  109. package/JavaScript/ui.d.ts +261 -0
  110. package/JavaScript/view-intents.d.ts +45 -0
  111. package/README.md +22 -3
  112. package/ThirdParty/OfficeIMO/LICENSE +21 -0
  113. package/ThirdParty/OfficeIMO/README.md +13 -0
  114. package/package.json +76 -3
@@ -1,31 +1,53 @@
1
- (function(global){"use strict";const installed=new WeakSet();const part={version:"606fcf6dcd00d420",install(context){if(installed.has(context.VIEW_MODES))return;const{VIEW_MODES,TreeGrid,h,setText,fill,numberFormat,foldText,cellText,cellValue,isCellObject,setAttr,toneOf,watchFlyout,densityRowHeight,layoutAnchor,layoutRows,layoutApplyTree,MAX_PROTOCOL_INTEGER,LAYOUT_DENSITIES,plural,svgIcon}=context;const CARD_METRICS={compact:{pad:8,field:36,gap:4,title:24},default:{pad:12,field:40,gap:6,title:26},comfortable:{pad:16,field:44,gap:8,title:28}};const CARD_FIELD=140,CARD_GUTTER=16,CARD_COLUMN_GAP=16,CARD_WIDE=["longText","tags","list"];function cardWide(column){return!!column.wrap||!!column.flex||CARD_WIDE.includes(column.presenter?.kind)}
2
- function cardGeometry(view){const metrics={...CARD_METRICS[view.density]},width=Math.max(0,(view.grid.clientWidth||view.host.clientWidth)- 2*CARD_GUTTER);const fields=(view.cols||[]).slice(1),most=Math.max(1,Math.floor((width+CARD_COLUMN_GAP)/ (CARD_FIELD + CARD_COLUMN_GAP)));
3
- const font=parseFloat(getComputedStyle(view.grid).fontSize),clampLines=column=>Math.min(column.maxLines??2,2);if(fields.some(column=>column.wrap))metrics.field=Math.max(metrics.field,Math.ceil(11.5*1.35+1+font*1.4*Math.max(...fields.filter(column=>column.wrap).map(clampLines))));if(view.cols?.[0]?.wrap)metrics.title=Math.max(metrics.title,Math.ceil((font+1)*1.4*clampLines(view.cols[0])));const slots=fields.reduce((sum,field)=>sum +(cardWide(field)&&most>1?2:1),0)||1;const columns=Math.min(most,Math.ceil(slots / Math.ceil(slots / most))), span = Math.min(2, columns), rows = [];
4
- for(const field of fields){const size=cardWide(field)?span:1;for(let row=0,placed=false;!placed;row++){const cells=rows[row]||=new Array(columns).fill(false);for(let at=0;at+size<=columns&&!placed;at++)if(cells.slice(at,at+size).every(used=>!used)){cells.fill(true,at,at+size);placed=true}}}
5
- const lines=rows.length;return{columns,span,metrics,height:2*metrics.pad+metrics.title +(lines?metrics.gap+lines*metrics.field +(lines-1)*metrics.gap:0)+ 1}}
6
- function cardStyle(view,geometry){const style=view.host.style,{metrics}=geometry;style.setProperty("--cx-card-cols",String(geometry.columns));style.setProperty("--cx-card-span",String(geometry.span));style.setProperty("--cx-card-pad",metrics.pad+"px");style.setProperty("--cx-card-field",metrics.field+"px");style.setProperty("--cx-card-gap",metrics.gap+"px");style.setProperty("--cx-card-title",metrics.title+"px")}
7
- function layoutApplyGrid(view,mode){const anchor=layoutAnchor(view);if(mode==="cards"){if(view.cardAutoRows===undefined){view.cardAutoRows=view.autoRows;view.autoRows=false}}
1
+ (function(global){"use strict";const installed=new WeakSet();const part={version:"077268d4fb78c464",install(context){if(installed.has(context.VIEW_MODES))return;const{VIEW_MODES,TreeGrid,h,setText,fill,numberFormat,foldText,cellText,cellValue,isCellObject,setAttr,toneOf,watchFlyout,densityRowHeight,layoutAnchor,layoutRows,layoutApplyTree,MAX_PROTOCOL_INTEGER,LAYOUT_DENSITIES,plural,svgIcon}=context;const CARD_METRICS={compact:{pad:12,field:36,gap:8,title:28},default:{pad:16,field:40,gap:10,title:30},comfortable:{pad:20,field:44,gap:12,title:32}};const CARD_MIN=280,CARD_GUTTER=16,CARD_COLUMN_GAP=16,CARD_WIDE=["longText","tags","list"];function cardWide(column){return!!column.wrap||!!column.flex||CARD_WIDE.includes(column.presenter?.kind)}
2
+ function cardRole(column,index,columns){if(!index)return "title";if(["enum","state"].includes(column.presenter?.kind))return "pill";if(index===columns.length-1&&column.kind==="datetime")return "footer";return cardWide(column)?"wide":"field"}
3
+ VIEW_MODES.cardRole=cardRole;function cardGeometry(view){const metrics={...CARD_METRICS[view.density]},available=Math.max(0,(view.grid.clientWidth||view.host.clientWidth)- 2*CARD_GUTTER);const lanes=view.rowProjection?1:Math.max(1,Math.floor((available+CARD_COLUMN_GAP)/ (CARD_MIN + CARD_COLUMN_GAP)));
4
+ const width=Math.max(0,(available -(lanes-1)*CARD_COLUMN_GAP)/ lanes), fields = (view.cols || []).slice(1);
5
+ const columns=width-2*metrics.pad>=264?2:1,span=columns;const font=parseFloat(getComputedStyle(view.grid).fontSize),clampLines=column=>Math.min(column.maxLines??2,2);if(fields.some(column=>column.wrap))metrics.field=Math.max(metrics.field,Math.ceil(11.5*1.35+1+font*1.4*Math.max(...fields.filter(column=>column.wrap).map(clampLines))));if(view.cols?.[0]?.wrap)metrics.title=Math.max(metrics.title,Math.ceil((font+1)*1.4*clampLines(view.cols[0])));let lines=0,used=0;fields.forEach((column,index)=>{const role=cardRole(column,index+1,view.cols),size=role==="field"?1:columns;if(used&&used+size>columns){lines++;used=0}
6
+ used +=size;if(used===columns){lines++;used=0}});if(used)lines++;const height=2*metrics.pad+metrics.title+lines*(metrics.field+metrics.gap)+ 2;return{columns,span,lanes,width,metrics,height,pitch:height+CARD_COLUMN_GAP}}
7
+ function cardStyle(view,geometry){const style=view.host.style,{metrics}=geometry;style.setProperty("--cx-card-cols",String(geometry.columns));style.setProperty("--cx-card-span",String(geometry.span));style.setProperty("--cx-card-pad",metrics.pad+"px");style.setProperty("--cx-card-field",metrics.field+"px");style.setProperty("--cx-card-gap",metrics.gap+"px");style.setProperty("--cx-card-title",metrics.title+"px");style.setProperty("--cx-card-width",geometry.width+"px");style.setProperty("--cx-card-height",geometry.height+"px");view.cardGeometry=geometry;cardMinHeight(view)}
8
+ function cardMinHeight(view){const count=view.rowProjection?view.rowProjection.rowCount():Math.max(0,view.rowBounds().last-view.pageOffset+1);const bands=Math.ceil(count / view.cardGeometry.lanes), chrome = [...view.host.children].reduce((height, child) => height + (child === view.grid ? 0 : child.offsetHeight), 2);
9
+ view.host.style.setProperty("--cx-min-h",chrome+Math.min(2,Math.max(1,bands))*view.cardGeometry.pitch+CARD_GUTTER+"px")}
10
+ function cardTotal(view){const bounds=view.rowBounds(),count=Math.max(0,bounds.last-bounds.first+1);return count?Math.ceil(count / view.cardGeometry.lanes) * view.rowH + CARD_GUTTER : 0;
11
+ }
12
+ function layoutCardRows(view,anchor){const ownedFocus=view.canvas.contains(document.activeElement);if(view.layoutMode==="cards"&&!view.rowProjection){for(const row of[...view.rowCache.values(),...view.placeholders.values()])row.hpx=view.rowH;view.total=cardTotal(view);view.syncScrollRange(anchor?view.rowTop(anchor.index)+ anchor.share*view.rowH:0);view.render()}else layoutRows(view,anchor);if(ownedFocus)layoutKeepFocus(view)}
13
+ function layoutKeepFocus(view){if(!view.focused||!view.canvas.contains(document.activeElement))return;cancelAnimationFrame(view.cardFocusFrame);view.cardFocusFrame=requestAnimationFrame(()=>{if(view.destroyed||!view.canvas.contains(document.activeElement))return;const rows=view.rowProjection||view,index=rows.indexOf(view.focused);if(index>=0){view.revealGridRow(view.focused,index,rows);view.render()}})}
14
+ function layoutApplyGrid(view,mode,anchor){if(mode==="cards"){if(view.cardAutoRows===undefined){view.cardAutoRows=view.autoRows;view.autoRows=false}}
8
15
  else if(view.cardAutoRows!==undefined){view.autoRows=view.cardAutoRows;view.cardAutoRows=undefined;view.rowOffsets=null}
9
16
  view.fitFont=null;view.fitKey=null;view.layout=(view.layout||0)+ 1;view.version++;view.layoutApplying=true;try{view.buildHeader();view.syncColumnControls()}finally{view.layoutApplying=false}
10
- if(mode==="cards"){const geometry=cardGeometry(view);cardStyle(view,geometry);view.rowH=geometry.height}
11
- else view.rowH=densityRowHeight(view,view.density);layoutRows(view,anchor);view.syncPinnedShadows?.()}
12
- function layoutApply(view,mode,density){const active=document.activeElement,held=view.canvas.contains(active)?active:null;const cell=held?.closest(".cx-cell"),row=view.focused,column=cell&&view.cols[view.rowCells(cell.parentNode).indexOf(cell)]?.id;view.layoutMode=mode;view.density=density;view.host.setAttribute("data-cx-density",density);if(mode==="fit")view.host.removeAttribute("data-cx-layout");else view.host.setAttribute("data-cx-layout",mode);view.host.style.setProperty("--cx-row-h",densityRowHeight(view,density)+ "px");if(view.kind==="grid")layoutApplyGrid(view,mode);else layoutApplyTree(view);if(held&&row&&view.rendered.get(row)){const at=column?view.cols.findIndex(item=>item.id===column):-1;if(at>=0)view.focusCell(row,at,null);else view.rendered.get(row).focus({preventScroll:true})}}
17
+ if(mode==="cards"){const geometry=cardGeometry(view);cardStyle(view,geometry);view.rowH=geometry.pitch}
18
+ else view.rowH=densityRowHeight(view,view.density);layoutCardRows(view,anchor);view.syncPinnedShadows?.()}
19
+ function layoutApply(view,mode,density){const active=document.activeElement,held=view.canvas.contains(active)?active:null,anchor=view.kind==="grid"?layoutAnchor(view):null;const cell=held?.closest(".cx-cell"),row=view.focused,column=cell&&view.cols[view.rowCells(cell.parentNode).indexOf(cell)]?.id;view.layoutMode=mode;view.density=density;view.host.setAttribute("data-cx-density",density);if(mode==="fit")view.host.removeAttribute("data-cx-layout");else view.host.setAttribute("data-cx-layout",mode);view.host.style.setProperty("--cx-row-h",densityRowHeight(view,density)+ "px");if(view.kind==="grid")layoutApplyGrid(view,mode,anchor);else layoutApplyTree(view);if(held&&row&&view.rendered.get(row)){const at=column?view.cols.findIndex(item=>item.id===column):-1;if(at>=0)view.focusCell(row,at,null);else view.rendered.get(row).focus({preventScroll:true})}}
20
+ function cardTemplateSlot(view,row,column,slot){const templates=view.view.cardTemplate;if(!templates||!Object.hasOwn(templates,column.id))return;const text=templates[column.id].replace(/\{([^{}]+)\}/g, (_, id) => {
21
+ const definition=view.columns.find(item=>item.id===id);return cellText(row.h.cells[id],definition,view.fmt)});slot.cell.classList.add("cx-card-templated");const content=slot.cardTemplateContent||=h("span","cx-card-slot");if(content.parentNode!==slot.cell)slot.cell.appendChild(content);const check=slot.cell.querySelector(".cx-grid-row-select");if(check&&check.parentNode!==slot.cell)slot.cell.prepend(check);setText(content,text);content.title=text}
13
22
  const LAYOUT_ICON='<rect x="4" y="4" width="16" height="7" rx="1.5"/><rect x="4" y="13" width="16" height="7" rx="1.5"/>';let layoutSeq=0;function layoutGroup(view,legend,name,choices,current,choose){const group=h("div","cx-layout-group"),title=h("div","cx-layout-title");title.id=name+"-title";group.setAttribute("role","radiogroup");group.setAttribute("aria-labelledby",title.id);setText(title,legend);group.appendChild(title);for(const[value,text]of choices){const label=h("label","cx-layout-choice"),input=h("input");input.type="radio";input.name=name;input.value=value;input.checked=value===current;input.addEventListener("change",()=>{if(input.checked)choose(value)});label.append(input,document.createTextNode(text));group.appendChild(label)}
14
23
  return group}
15
24
  function layoutModeText(view,mode){return view.labels[mode==="fit"?"layoutFit":mode==="scroll"?"layoutScroll":"layoutCards"]}
16
25
  function layoutDensityText(view,density){return view.labels[density==="compact"?"layoutCompact":density==="comfortable"?"layoutComfortable":"layoutNormal"]}
17
26
  function layoutMenu(view){if(view.tools.layout===false||typeof view.controlPanel!=="function")return;const{details,panel}=view.controlPanel(view.labels.layoutMenu,"cx-grid-layout"),id=view.id+"-layout-" + ++layoutSeq;const icon=details.firstChild.querySelector("svg");if(icon)icon.replaceWith(svgIcon(LAYOUT_ICON));const choose=change=>Promise.resolve().then(()=>VIEW_MODES.setLayout(view,change)).catch(error=>view.emit("error",{error}));const render=()=>{const options=view.layoutOptions;if(panel.firstChild){for(const input of panel.querySelectorAll("input"))input.checked=input.value===view.layoutMode||input.value===view.density;return}
18
27
  panel.replaceChildren(...[options.modes.length>1&&layoutGroup(view,view.labels.layoutMenu,id+"-mode",options.modes.map(mode=>[mode,layoutModeText(view,mode)]),view.layoutMode,mode=>choose({mode})),layoutGroup(view,view.labels.layoutDensity,id+"-density",LAYOUT_DENSITIES.map(density=>[density,layoutDensityText(view,density)]),view.density,density=>choose({density}))].filter(Boolean))};render();view.layoutMenu={details,render};const anchor=view.toolbar.querySelector(".cx-grid-views")||view.spacer;if(anchor&&anchor.parentNode===view.toolbar)view.toolbar.insertBefore(details,anchor)}
19
- VIEW_MODES.layoutsStart=view=>{if(!view.layoutMenu)layoutMenu(view);if(!view.sortMenu)sortMenu(view);if(view.layoutStarted)return;view.layoutStarted=true;view.host.classList.add("cx-layouts");view.lifetime.signal.addEventListener("abort",()=>{view.host.classList.remove("cx-layouts");for(const name of["cols","span","pad","field","gap","title"])view.host.style.removeProperty("--cx-card-"+name)},{once:true});view.host.setAttribute("data-cx-density",view.density);if(view.layoutMode!=="fit"||view.kind==="grid"&&view.density!==view.declaredDensity)layoutApply(view,view.layoutMode,view.density)};VIEW_MODES.refreshCardDensity=view=>{const anchor=layoutAnchor(view),geometry=cardGeometry(view);cardStyle(view,geometry);view.rowH=geometry.height;view.version++;layoutRows(view,anchor)};VIEW_MODES.setLayout=(view,change)=>{if(!change||typeof change!=="object"||Array.isArray(change)||Object.keys(change).some(key=>!["mode","density"].includes(key))||
28
+ VIEW_MODES.layoutsStart=view=>{if(view.layoutOptions&&!view.layoutMenu)layoutMenu(view);if(view.layoutOptions&&!view.sortMenu)sortMenu(view);if(view.kind==="grid"&&view.view.stickyHeader)pageHeaderStart(view);if(view.layoutStarted)return;view.layoutStarted=true;view.host.classList.add("cx-layouts");view.lifetime.signal.addEventListener("abort",()=>{cancelAnimationFrame(view.cardFocusFrame);view.host.classList.remove("cx-layouts");for(const name of["cols","span","pad","field","gap","title","width","height"])view.host.style.removeProperty("--cx-card-"+name)},{once:true});view.host.setAttribute("data-cx-density",view.density);if(view.layoutMode!=="fit"||view.kind==="grid"&&view.density!==view.declaredDensity)layoutApply(view,view.layoutMode,view.density)};VIEW_MODES.refreshCardDensity=view=>{const anchor=layoutAnchor(view),geometry=cardGeometry(view);cardStyle(view,geometry);view.rowH=geometry.pitch;view.version++;layoutCardRows(view,anchor)};VIEW_MODES.setLayout=(view,change)=>{if(!change||typeof change!=="object"||Array.isArray(change)||Object.keys(change).some(key=>!["mode","density"].includes(key))||
20
29
  change.mode!==undefined&&!view.layoutOptions.modes.includes(change.mode)||change.density!==undefined&&!LAYOUT_DENSITIES.includes(change.density))
21
30
  throw new Error("CanopyX: setLayout takes a mode the view allows and a density.");if(!view.layoutStarted)VIEW_MODES.layoutsStart(view);const mode=change.mode??view.layoutMode,density=change.density??view.density;if(mode!==view.layoutMode||density!==view.density){layoutApply(view,mode,density);view.layoutMenu?.render();view.sortMenu?.sync();if(view.live){setText(view.live,"");setText(view.live,fill(view.labels.layoutChanged,{mode:layoutModeText(view,mode),density:layoutDensityText(view,density)}))}
22
31
  view.stateChanged();view.emit("layout",{mode,density})}
23
- return Promise.resolve(view.getLayout())};if(VIEW_MODES.DataGrid){const proto=VIEW_MODES.DataGrid.prototype,paint=proto.fillRow,widths=proto.applyWidths;proto.fillRow=function(element,row){paint.call(this,element,row);if(this.layoutMode!=="cards"||!element._cells||row.status||row.group)return;element._cells.forEach((slot,index)=>{const column=this.cols[index],cell=slot&&slot.cell;if(!column||!cell)return;const label=index?column.title:null,wide=index&&cardWide(column)?"wide":null;if((cell.getAttribute("data-cx-label")??null)!==label){if(label===null)cell.removeAttribute("data-cx-label");else cell.setAttribute("data-cx-label",label)}
24
- if((cell.getAttribute("data-cx-card")??null)!==wide){if(wide)cell.setAttribute("data-cx-card",wide);else cell.removeAttribute("data-cx-card")}})};proto.applyWidths=function(...args){const result=widths.apply(this,args);if(this.layoutMode==="cards"&&!this.layoutApplying&&this.cols&&this.sized){const geometry=cardGeometry(this);cardStyle(this,geometry);if(geometry.height!==this.rowH){const anchor=layoutAnchor(this);this.rowH=geometry.height;this.layout=(this.layout||0)+ 1;this.version++;layoutRows(this,anchor)}}
32
+ return Promise.resolve(view.getLayout())};if(VIEW_MODES.DataGrid){const proto=VIEW_MODES.DataGrid.prototype,paint=proto.fillRow,widths=proto.applyWidths;const rowTop=proto.rowTop,rowAt=proto.rowAt,renderPass=proto.renderPass,pageStep=proto.gridPageStep;const tiled=view=>view.layoutMode==="cards"&&view.cardGeometry&&!view.rowProjection;proto.rowTop=function(index){if(!tiled(this))return rowTop.call(this,index);return CARD_GUTTER+Math.floor((index-this.pageOffset)/ this.cardGeometry.lanes) * this.rowH;
33
+ };proto.rowAt=function(top){if(!tiled(this))return rowAt.call(this,top);const bounds=this.rowBounds();return Math.max(bounds.first,Math.min(bounds.last,bounds.first+Math.max(0,Math.floor((top-CARD_GUTTER)/ this.rowH)) * this.cardGeometry.lanes));
34
+ };proto.gridPageStep=function(row,direction,...args){if(!tiled(this))return pageStep.call(this,row,direction,...args);return row._ri+direction*this.cardGeometry.lanes*Math.max(1,Math.floor((this.grid.clientHeight-this.head.offsetHeight)/ this.rowH));
35
+ };proto.renderPass=function(...args){const cards=this.layoutMode==="cards"&&this.cardGeometry;if(cards&&!!this.rowProjection!==!!this.cardProjection){const geometry=cardGeometry(this);if(geometry.lanes!==this.cardGeometry.lanes||geometry.height!==this.cardGeometry.height){cardStyle(this,geometry);this.rowH=geometry.pitch;this.layout=(this.layout||0)+ 1;this.version++}
36
+ this.cardProjection=!!this.rowProjection}
37
+ const tile=tiled(this),viewport=this.viewportHeight;if(cards)cardMinHeight(this);if(tile){const total=cardTotal(this);if(this.total!==total){this.total=total;this.syncScrollRange(this.logicalTop())}
38
+ this.viewportHeight=(viewport??this.grid.clientHeight-this.head.offsetHeight)+ this.rowH}
39
+ try{return renderPass.apply(this,args)}
40
+ finally{this.viewportHeight=viewport;for(const[row,element]of this.rendered){const lane=tile?(row._ri-this.pageOffset)%this.cardGeometry.lanes:0;element.style.left=cards&&!row.group&&!row.status?CARD_GUTTER+lane*(this.cardGeometry.width+CARD_COLUMN_GAP)+ "px":"";if(cards&&tile&&row.status){element.style.left=CARD_GUTTER+lane*(this.cardGeometry.width+CARD_COLUMN_GAP)+ "px";element.style.width=this.cardGeometry.width+"px"}}}};proto.fillRow=function(element,row){paint.call(this,element,row);if(this.layoutMode!=="cards"||!element._cells||row.status||row.group)return;element._cells.forEach((slot,index)=>{const column=this.cols[index],cell=slot&&slot.cell;if(!column||!cell)return;const label=index?column.title:null,wide=cardRole(column,index,this.cols);if((cell.getAttribute("data-cx-label")??null)!==label){if(label===null)cell.removeAttribute("data-cx-label");else cell.setAttribute("data-cx-label",label)}
41
+ if((cell.getAttribute("data-cx-card")??null)!==wide){if(wide)cell.setAttribute("data-cx-card",wide);else cell.removeAttribute("data-cx-card")}
42
+ cardTemplateSlot(this,row,column,slot)})};proto.applyWidths=function(...args){const result=widths.apply(this,args);if(this.layoutMode==="cards"&&!this.layoutApplying&&this.cols&&this.sized){const anchor=layoutAnchor(this),previous=this.cardGeometry,geometry=cardGeometry(this);cardStyle(this,geometry);if(geometry.pitch!==this.rowH||geometry.lanes!==previous?.lanes||geometry.width!==previous?.width){this.rowH=geometry.pitch;this.layout=(this.layout||0)+ 1;this.version++;layoutCardRows(this,anchor)}
43
+ layoutKeepFocus(this)}
25
44
  return result}}
26
45
  const SORT_ICON='<path d="M8 5v14M4.5 15.5 8 19l3.5-3.5M16 19V5M12.5 8.5 16 5l3.5 3.5"/>';function sortMenuRender(view,panel){const query=view.acceptedQuery,sorts=query?query.sort:[],parts=[];if(view.selectionControls)parts.push(view.selectionControls);const list=h("div","cx-sort-list");list.setAttribute("role","group");list.setAttribute("aria-label",view.labels.layoutSort);for(const column of view.cols||[]){if(!column.sortable&&view.tools.columnMenus===false)continue;const row=h("div","cx-sort-choice"),at=sorts.findIndex(sort=>sort.column===column.id),sort=sorts[at];if(column.sortable){const button=h("button","cx-button cx-sort-column"),arrow=sort?sort.direction==="asc"?" \u2191":" \u2193":"";button.type="button";setText(button,column.title+arrow +(sort&&sorts.length>1?" " +(at+1):""));button.setAttribute("aria-pressed",String(!!sort));button.setAttribute("aria-label",column.title +(sort?", "+view.labels[sort.direction==="asc"?"gridSortAscending":"gridSortDescending"]:""));button.addEventListener("click",event=>view.querySortHeader(column.id,event));row.appendChild(button)}else{const title=h("span","cx-sort-column");setText(title,column.title);row.appendChild(title)}
27
- if(view.tools.columnMenus!==false&&typeof view.openColumnMenu==="function"){const menu=h("button","cx-button icon cx-sort-menu");menu.type="button";menu.setAttribute("aria-label",fill(view.labels.gridColumnMenu,{column:column.title}));menu.setAttribute("aria-haspopup","menu");menu.appendChild(document.createTextNode("\u22ef"));menu.addEventListener("click",()=>view.openColumnMenu(column.id,menu));row.appendChild(menu)}
46
+ if(view.tools.columnMenus!==false&&typeof view.openColumnMenu==="function"){const menu=h("button","cx-button icon cx-sort-menu");menu.type="button";menu.dataset.cxSortColumnMenu=column.id;menu.setAttribute("aria-expanded","false");view.adoptColumnAnchor(column.id,"data-cx-sort-column-menu",menu);menu.setAttribute("aria-label",fill(view.labels.gridColumnMenu,{column:column.title}));menu.setAttribute("aria-haspopup","menu");menu.appendChild(document.createTextNode("\u22ef"));menu.addEventListener("click",()=>view.openColumnMenu(column.id,menu));row.appendChild(menu)}
28
47
  list.appendChild(row)}
29
48
  parts.push(list);panel.replaceChildren(...parts)}
30
49
  function sortMenu(view){if(view.kind!=="grid"||view.tools.layout===false||!view.layoutOptions.modes.includes("cards")||typeof view.controlPanel!=="function")return;const{details,panel}=view.controlPanel(view.labels.layoutSort,"cx-grid-sort",target=>sortMenuRender(view,target));const icon=details.firstChild.querySelector("svg");if(icon)icon.replaceWith(svgIcon(SORT_ICON));const anchor=view.layoutMenu?.details||view.toolbar.querySelector(".cx-grid-views")||view.spacer;if(anchor&&anchor.parentNode===view.toolbar)view.toolbar.insertBefore(details,anchor);view.host.addEventListener("canopyx:query",()=>{if(details.open)sortMenuRender(view,panel)},{signal:view.lifetime.signal});view.sortMenu={details,sync:()=>{details.hidden=view.layoutMode!=="cards";if(details.hidden)details.open=false}};view.sortMenu.sync()}
50
+ function pageHeaderStart(view){if(view.pageHeader||view.view.autoHeight===false)return;const head=view.head,grid=view.grid,signal=view.lifetime.signal,names=["position","top","left","width","min-width","clip-path","z-index"],previous=names.map(name=>[name,head.style.getPropertyValue(name),head.style.getPropertyPriority(name)]),spacer=h("div","cx-page-header-space");spacer.setAttribute("aria-hidden","true");spacer.hidden=true;grid.insertBefore(spacer,head);let frame=0,fixed=false;const restore=()=>{if(!fixed)return;fixed=false;spacer.hidden=true;for(const[name,value,priority]of previous){if(value)head.style.setProperty(name,value,priority);else head.style.removeProperty(name)}
51
+ view.host.removeAttribute("data-cx-page-header")};const update=()=>{frame=0;const box=grid.getBoundingClientRect(),height=head.offsetHeight,top=Math.max(0,parseFloat(getComputedStyle(view.host).getPropertyValue("--cx-sticky-top"))||0),fits=grid.scrollHeight<=grid.clientHeight+1;if(view.destroyed||view.layoutMode==="cards"||!height||!fits||box.top>=top||box.bottom<=top||!grid.checkVisibility()){restore();return}
52
+ fixed=true;spacer.hidden=false;spacer.style.height=height+"px";const width=Math.max(grid.clientWidth,view.rowWidth||0),left=box.left+grid.clientLeft-grid.scrollLeft,clipLeft=Math.max(0,grid.scrollLeft),clipRight=Math.max(0,width-grid.clientWidth-clipLeft);Object.assign(head.style,{position:"fixed",top:Math.min(top,box.bottom-height)+ "px",left:left+"px",width:width+"px",minWidth:width+"px",clipPath:"inset(0 "+clipRight+"px 0 "+clipLeft+"px)",zIndex:"20"});view.host.setAttribute("data-cx-page-header","true");view.gridPopover?.position()};const schedule=()=>{if(!frame)frame=requestAnimationFrame(update)};const observer=new ResizeObserver(schedule);for(const element of[grid,head,view.host])observer.observe(element);window.addEventListener("scroll",schedule,{capture:true,passive:true,signal});window.addEventListener("resize",schedule,{passive:true,signal});for(const name of["query","layout"])view.host.addEventListener("canopyx:"+name,schedule,{signal});signal.addEventListener("abort",()=>{cancelAnimationFrame(frame);observer.disconnect();restore();spacer.remove()},{once:true});view.pageHeader={schedule};schedule()}
31
53
  installed.add(context.VIEW_MODES)}};(global.CanopyXParts||=[]).push(["layouts",part])})(typeof window!=="undefined"?window:globalThis);
@@ -1,4 +1,4 @@
1
- (function(global){"use strict";const installed=new WeakSet();const part={version:"e7268bbe59e2dfa8",install(context){if(installed.has(context.LINK_IMPL))return;const{LINK_IMPL,h,setText,filterChipContent,fill,numberFormat,cellValue,setAttr,cellMatches}=context;const{TreeGrid,DataGrid}=LINK_IMPL;const LINK_SOURCE=/^[A-Za-z0-9][\w.:-]{0,127}$/, LINK_MAX_KEYS = 10000, LINK_MAX_VALUES = 1000;
1
+ (function(global){"use strict";const installed=new WeakSet();const part={version:"fd21dd4495607a16",install(context){if(installed.has(context.LINK_IMPL))return;const{LINK_IMPL,h,setText,filterChipContent,fill,numberFormat,cellValue,setAttr,cellMatches}=context;const{TreeGrid,DataGrid}=LINK_IMPL;const LINK_SOURCE=/^[A-Za-z0-9][\w.:-]{0,127}$/, LINK_MAX_KEYS = 10000, LINK_MAX_VALUES = 1000;
2
2
  const linkText=(value,name,max)=>{if(value===undefined)return undefined;if(typeof value!=="string"||!/\S/.test(value) || value.length > max) throw new Error("CanopyX: link " + name + " must be visible text of at most " + max + " characters.");
3
3
  return value};const linkIsoDate=value=>typeof value==="string"&&/^\d{4}-\d\d-\d\dT.*(?:Z|[+-]\d\d:\d\d)$/.test(value) && Number.isFinite(Date.parse(value));
4
4
  function linkColumn(grid,field){const column=grid.view.columns.find(item=>item.id===field);if(!column)throw new Error("CanopyX: a link names the undeclared column '"+field+"'.");return column}
@@ -16,7 +16,7 @@ fill(this.labels.linkFiltered,{source:last.sourceLabel,count:numberFormat(this.l
16
16
  catch(error){this.emit("error",{error})}}};Object.assign(TreeGrid.prototype,LINK_COMMON);if(DataGrid)Object.assign(DataGrid.prototype,LINK_COMMON);if(DataGrid){const{gridQueryStart}=LINK_IMPL;const proto=DataGrid.prototype,SET_QUERY=proto.setQuery,FOR_CHANGE=proto.queryForChange,GET_STATE=proto.getState;const SUMMARY=proto.syncQuerySummary,CHROME=proto.syncGridChrome,NOTICE=proto.setNotice,DESTROY=proto.destroy;const same=(a,b)=>JSON.stringify(a)===JSON.stringify(b),LINK_OWN=LINK_IMPL.queryOwn;const strip=(filters,tail)=>{filters=filters||[];if(!tail||!tail.length||filters.length<tail.length)return filters;return tail.every((filter,index)=>same(filters[filters.length-tail.length+index],filter))?filters.slice(0,filters.length-tail.length):filters};const tailOf=(grid,pending)=>pending&&grid.pendingQuery&&grid.linkSent?grid.linkSent.ext:grid.linkAcceptedExt;Object.assign(proto,{setQuery(input,paging){const desired=this.linkFilters||[];if(!desired.length&&!this.linkAcceptedExt&&!this.linkSent)return SET_QUERY.call(this,input,paging);const ext=linkPredicates(desired),own=!input||!input.filters?[]:input[LINK_OWN]?input.filters:strip(input.filters,tailOf(this,true));const before=this.queryEpoch,promise=SET_QUERY.call(this,{...(input||{}),filters:own.concat(ext),[LINK_OWN]:undefined},paging);if(this.queryEpoch!==before)this.linkSent={epoch:this.queryEpoch,ext,desired:desired.slice()};return promise.catch(error=>{if((!error||error.name!=="AbortError")&&!this.destroyed&&!this.pendingQuery){if(this.lastFailedQuery&&this.linkSent?.epoch===this.queryEpoch)
17
17
  this.lastFailedQuery={...this.lastFailedQuery,filters:strip(this.lastFailedQuery.filters,this.linkSent.ext),[LINK_OWN]:true};this.linkFilters=(this.linkCommitted||[]).slice();this.linkReason=null;this.linkSent=null;this.syncQuerySummary()}
18
18
  throw error})},queryForChange(includeQueuedInput,includeExternal){const query=FOR_CHANGE.call(this,includeQueuedInput),tail=tailOf(this,true);if(includeExternal)return query;return tail&&query.filters?{...query,filters:strip(query.filters,tail),[LINK_OWN]:true}:query},getState(){const state=GET_STATE.call(this);if(!this.linkAcceptedExt)return state;const own=strip(state.query.filters,this.linkAcceptedExt);const kept=linkPredicates((this.linkCommitted||[]).filter(filter=>filter.persist)).filter(filter=>!own.some(item=>same(item,filter)));return{...state,query:{...state.query,filters:own.concat(kept)}}},async linkCommit(next,reason){this.linkFilters=next;this.linkReason=reason;if(!this.acceptedQuery&&!this.pendingQuery)await this.ready;try{await this.setQuery(gridQueryStart(this.queryForChange(true)));return true}
19
- catch(error){if(error&&error.name==="AbortError")return false;throw error}},linkClearAll(){this.linkFilters=[];this.linkReason="user"},linkCount(){return this.snapshot?this.snapshot.matched:0},linkChips(){this.syncQuerySummary()},syncGridChrome(){CHROME.call(this);const summary=this.linkAcceptedExt&&this.acceptedQuery&&this.toolbar.querySelector(".cx-grid-filters > summary");if(!summary)return;const group=item=>item?item.filters.length+item.groups.reduce((sum,child)=>sum+group(child),0):0;const count=strip(this.acceptedQuery.filters,this.linkAcceptedExt).length+group(this.acceptedQuery.filterGroup);summary.classList.toggle("active",!!count);summary.querySelector(".badge")?.remove();if(count){const badge=h("span","badge");setText(badge,String(count));summary.appendChild(badge)}},syncQuerySummary(){let changed=false;if(this.acceptedQuery&&this.acceptedQuery!==this.linkSeenQuery){this.linkSeenQuery=this.acceptedQuery;const sent=this.linkSent&&this.linkSent.epoch===this.queryEpoch?this.linkSent:null;if(sent){this.linkAcceptedExt=sent.ext;changed=!same(this.linkCommitted||[],sent.desired);this.linkCommitted=sent.desired}}
19
+ catch(error){if(error&&error.name==="AbortError")return false;throw error}},linkClearAll(){this.linkFilters=[];this.linkReason="user"},linkCount(){return this.snapshot?this.snapshot.matched:0},linkChips(){this.syncQuerySummary()},syncGridChrome(){CHROME.call(this);const summary=this.linkAcceptedExt&&this.acceptedQuery&&this.toolbar.querySelector(".cx-grid-filters > summary");if(!summary)return;const group=item=>item?item.filters.length+item.groups.reduce((sum,child)=>sum+group(child),0):0;const count=strip(this.acceptedQuery.filters,this.linkAcceptedExt).length+group(this.acceptedQuery.filterGroup)+(this.acceptedQuery.tupleFilters?.length||0);summary.classList.toggle("active",!!count);summary.querySelector(".badge")?.remove();if(count){const badge=h("span","badge");setText(badge,String(count));summary.appendChild(badge)}},syncQuerySummary(){let changed=false;if(this.acceptedQuery&&this.acceptedQuery!==this.linkSeenQuery){this.linkSeenQuery=this.acceptedQuery;const sent=this.linkSent&&this.linkSent.epoch===this.queryEpoch?this.linkSent:null;if(sent){this.linkAcceptedExt=sent.ext;changed=!same(this.linkCommitted||[],sent.desired);this.linkCommitted=sent.desired}}
20
20
  const active=document.activeElement,key=this.queryChips&&this.queryChips.contains(active)?active.getAttribute("data-cx-condition"):null;SUMMARY.call(this);if(this.queryChips&&this.acceptedQuery&&this.linkAcceptedExt){const tail=this.linkAcceptedExt.length,filters=this.acceptedQuery.filters;for(let index=filters.length-tail;index<filters.length;index++)this.queryChips.querySelector('[data-cx-condition="filter-'+index+'"]')?.remove();const chips=this.linkSources().map(source=>this.linkChip(source,()=>this.linkRemove(source)));this.queryChips.prepend(...chips);const clear=this.queryChips.querySelector('[data-cx-condition="clear"]');if(clear&&chips.length){const run=clear.onclick;clear.onclick=event=>{this.linkClearAll();run(event)}}
21
21
  if(key&&key.startsWith("link-")){const target=[...this.queryChips.children].find(item=>item.getAttribute("data-cx-condition")===key)||this.queryChips.querySelector(".cx-link-chip, .cx-grid-chip");(target||this.searchInput||this.grid).focus({preventScroll:true})}}
22
22
  if(changed){const reason=this.linkReason;this.linkReason=null;this.linkChanged(reason)}},setNotice(...args){NOTICE.apply(this,args);const clear=(this.linkFilters?.length||this.linkCommitted?.length)&&this.notice.querySelector(".acts button");if(clear){const run=clear.onclick;clear.onclick=event=>{this.linkClearAll();run(event)}}},destroy(){this.host.classList.remove("cx-has-linked");return DESTROY.call(this)}})}
@@ -5,9 +5,7 @@
5
5
  .cx-data-grid .cx-live-bar.cx-live-float{position:absolute;left:50%;z-index:4;transform:translateX(-50%);max-width:calc(100% - var(--cx-size-24px));border:var(--cx-size-1px) solid var(--hfx-color-border-strong,light-dark(#cdd0d5,#363a42));background:var(--hfx-color-surface,light-dark(#ffffff,#16181c));box-shadow:var(--hfx-elevation-2,0 1px 2px rgba(22,24,28,.05),0 6px 18px -10px rgba(22,24,28,.18));}
6
6
  .cx-data-grid .cx-live-bar .cx-ico{flex:none;width:var(--cx-size-16px);height:var(--cx-size-16px);color:var(--hfx-color-accent,light-dark(#11776f,#37c9b8));}
7
7
  .cx-data-grid .cx-live-bar-text{overflow:hidden;text-overflow:ellipsis;}
8
- .cx-data-grid .cx-live-bar .cx-button{height:var(--cx-size-28px);min-height:var(--cx-size-28px);padding:0 var(--cx-size-10px);border:var(--cx-size-1px) solid color-mix(in srgb,var(--hfx-color-accent,light-dark(#11776f,#37c9b8)) 45%,var(--hfx-color-surface,light-dark(#ffffff,#16181c)));border-radius:var(--hfx-radius-sm,4px);background:var(--hfx-color-surface,light-dark(#ffffff,#16181c));color:var(--hfx-color-text,light-dark(#16181c,#f2f3f5));font:inherit;font-size:var(--cx-size-13px);font-weight:600;cursor:pointer;}
9
- .cx-data-grid .cx-live-bar .cx-button:hover{background:color-mix(in srgb,var(--hfx-color-accent,light-dark(#11776f,#37c9b8)) 10%,var(--hfx-color-surface,light-dark(#ffffff,#16181c)));}
10
- .cx-data-grid .cx-live-bar .cx-button: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-data-grid .cx-live-bar .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);}
11
9
  .cx-data-grid.cx-phone .cx-live-bar{height:var(--cx-size-44px);padding:0 0 0 var(--cx-size-8px);background:transparent;}
12
10
  .cx-data-grid.cx-phone .cx-live-bar:not(.cx-live-float) .cx-live-bar-text{position:absolute;width:var(--cx-size-1px);height:var(--cx-size-1px);overflow:hidden;clip-path:inset(50%);white-space:nowrap;}
13
11
  .cx-data-grid.cx-phone .cx-live-bar .cx-button{height:var(--cx-size-44px);min-height:var(--cx-size-44px);min-width:var(--cx-size-44px);background:var(--hfx-color-accent-subtle,light-dark(#e7f1f1,#1b3435));}
@@ -1,4 +1,4 @@
1
- (function(global){"use strict";const installed=new WeakSet();const part={version:"3b3c9833ff68a72b",install(context){if(installed.has(context.DataGrid.prototype))return;const{DataGrid,h,setText,fill,gridButton,gridIcon,gridQuery,gridResultShape,gridQueryRows,gridCompare,gridOrder,gridIdOrder,validateGridRows,validateGridPage,validateGridCursorPage,gridFreeze,requireIdentifier,checkDocument,cellValue,PROTOCOL,GRID_REPORTING,VIEW_MODES,inlineGridSource}=context;const LIVE_KEYS=new Set(["stableOrder","flash","batchMs","pollMs","load","announce","pauseWhenHidden"]);const LIVE_MAX_ROWS=100000;function liveOptions(view,options){const read=value=>{if(value===undefined||value===true||value===false)return{};if(!value||typeof value!=="object"||Array.isArray(value)||Object.keys(value).some(key=>!LIVE_KEYS.has(key)))
1
+ (function(global){"use strict";const installed=new WeakSet();const part={version:"4ef2fb01714f877e",install(context){if(installed.has(context.DataGrid.prototype))return;const{DataGrid,h,setText,fill,gridButton,gridIcon,gridQuery,gridResultShape,gridQueryRows,gridCompare,gridOrder,gridIdOrder,validateGridRows,validateGridPage,validateGridCursorPage,gridFreeze,requireIdentifier,checkDocument,cellValue,PROTOCOL,GRID_REPORTING,GRID_CALCULATIONS,VIEW_MODES,inlineGridSource}=context;const LIVE_KEYS=new Set(["stableOrder","flash","batchMs","pollMs","load","announce","pauseWhenHidden"]);const LIVE_MAX_ROWS=100000;function liveOptions(view,options){const read=value=>{if(value===undefined||value===true||value===false)return{};if(!value||typeof value!=="object"||Array.isArray(value)||Object.keys(value).some(key=>!LIVE_KEYS.has(key)))
2
2
  throw new Error("CanopyX: live takes stableOrder, flash, batchMs, pollMs, load, announce and pauseWhenHidden.");return value};const merged=options.live===false?{}:{...read(view.live),...read(options.live)},flash=merged.flash;const integer=(value,min,max)=>value===undefined||Number.isInteger(value)&&value>=min&&value<=max;if(["stableOrder","announce","pauseWhenHidden"].some(key=>merged[key]!==undefined&&typeof merged[key]!=="boolean")||
3
3
  !(flash===undefined||typeof flash==="boolean"||flash&&typeof flash==="object"&&Object.keys(flash).every(key=>key==="durationMs")&&integer(flash.durationMs,100,10000))||
4
4
  !integer(merged.batchMs,0,60000)||!(merged.pollMs===undefined||merged.pollMs===0||integer(merged.pollMs,250,86400000))||
@@ -27,7 +27,7 @@ return this.liveRowMap},liveOrderOf(){const query=this.acceptedQuery,shape=gridR
27
27
  return this.liveOrder},liveApplyRows(queue,ordered){const rows=this.liveRows(),query=this.acceptedQuery,stable=this.liveOpts.stableOrder;if(!ordered)this.liveOrder=null;const order=ordered?this.liveOrderOf():{at:new Map(),ids:[]};const matches=liveMatcher(this,query),compare=liveComparer(this,query);const result={added:[],updated:[],removed:[],missing:[]},changed=new Map(),leave=new Set(),enter=[],kept=[];for(const change of queue.values()){const before=rows.get(change.id);if(change.kind==="remove"){if(!before){result.missing.push(change.id);continue}
28
28
  rows.delete(change.id);result.removed.push(change.id);if(order.at.has(change.id))leave.add(change.id);continue}
29
29
  if(change.kind==="update"&&!before){result.missing.push(change.id);continue}
30
- const after=change.kind==="add"?change.row:liveMerged(before,change.row);if(before&&liveSameRow(before,after))continue;rows.set(change.id,after);(before?result.updated:result.added).push(change.id);changed.set(change.id,liveChangedColumns(before,after));if(!ordered)continue;const was=order.at.has(change.id),now=matches(after);if(was&&!now)leave.add(change.id);else if(!was&&now)enter.push(after);else if(was&&now){if(stable)kept.push(change.id);else{leave.add(change.id);enter.push(after)}}}
30
+ const after=change.kind==="add"?change.row:liveMerged(before,change.row);GRID_CALCULATIONS.apply?.(this.view,[after]);if(before&&liveSameRow(before,after))continue;rows.set(change.id,after);(before?result.updated:result.added).push(change.id);changed.set(change.id,liveChangedColumns(before,after));if(!ordered)continue;const was=order.at.has(change.id),now=matches(after);if(was&&!now)leave.add(change.id);else if(!was&&now)enter.push(after);else if(was&&now){if(stable)kept.push(change.id);else{leave.add(change.id);enter.push(after)}}}
31
31
  if(!changed.size&&!result.removed.length)return{result,changed};if(!ordered){this.view.rows=this.liveRowsOf=[...rows.values()];return{result,changed}}
32
32
  let dirty=order.dirty;const neighbour=(index,step)=>{for(let at=index+step;at>=0&&at<order.ids.length;at +=step)if(!leave.has(order.ids[at]))return rows.get(order.ids[at]);return null};for(const id of kept){if(dirty)break;const index=order.at.get(id),row=rows.get(id),previous=neighbour(index,-1),next=neighbour(index,1);dirty=!!(previous&&compare(previous,row)>0||next&&compare(row,next)>0)}
33
33
  let ids=leave.size?order.ids.filter(id=>!leave.has(id)):order.ids;if(enter.length){enter.sort(compare);const merged=[];let i=0,j=0;while(i<ids.length&&j<enter.length)merged.push(compare(rows.get(ids[i]),enter[j])<=0?ids[i++]:enter[j++].id);while(i<ids.length)merged.push(ids[i++]);while(j<enter.length)merged.push(enter[j++].id);ids=merged}
@@ -53,7 +53,7 @@ for(let page=Math.floor(Math.max(bounds.first,range.first)/ size) * size; page <
53
53
  return[...offsets].slice(0,4).sort((a,b)=>a-b)},async liveRefreshHost(force,signal=this.dataLifetime.signal){if(this.cursor)return this.liveRefreshCursor(signal);const offsets=this.liveHeldOffsets(),lifetime=this.dataLifetime,epoch=this.queryEpoch,accepted=this.acceptedQuery;const ask=async(offset,snapshot)=>{const query=gridQuery(this.view,{...accepted,offset,limit:this.pageSize,revision:snapshot?snapshot.revision:undefined});const page=validateGridPage(await this.requestPage(query,signal),this.view,query,snapshot);if(signal.aborted||this.destroyed||epoch!==this.queryEpoch||this.acceptedQuery!==accepted)throw new DOMException("The refresh was superseded.","AbortError");return page};const first=await ask(0,null);if(!force&&(first.revision===this.snapshot.revision||first.revision===this.livePending?.snapshot?.revision))return null;const snapshot={revision:first.revision,total:first.total,matched:first.matched};if(this.liveProjected()){this.livePending=null;this.liveMoved=false;await this.liveRequery(new Map(),[],signal);return{changed:new Map(),removed:[],orderChanged:false,revision:this.snapshot.revision}}
54
54
  const pages=new Map([[0,first.items]]);for(const offset of offsets)if(offset&&offset<snapshot.matched)pages.set(offset,(await ask(offset,snapshot)).items);const position=new Map(),fresh=new Map();for(const[offset,items]of pages)items.forEach((item,index)=>{position.set(item.id,offset+index);fresh.set(item.id,item)});const changed=new Map(),held=new Map();for(const row of this.rowCache.values())if(!row.status)held.set(row.key,row);for(const[id,item]of fresh){const row=held.get(id),columns=row?liveChangedColumns(row.h,item):"*";if(columns==="*"||columns.size)changed.set(id,columns)}
55
55
  const seen=[...this.rendered.keys()].filter(row=>!row.status&&row.key).sort((a,b)=>a._ri-b._ri).map(row=>row.key);const before=seen.filter(id=>position.has(id)),after=[...before].sort((a,b)=>position.get(a)- position.get(b));const moved=before.some((id,index)=>id!==after[index]);if(moved&&this.liveOpts.stableOrder&&!force){this.liveSetHeld(fresh,false);this.livePending={snapshot,pages};this.liveSyncPrompt();return{changed,removed:[],orderChanged:true,revision:snapshot.revision}}
56
- this.livePending=null;this.liveMoved=false;this.liveSwap(snapshot,()=>pages,id=>position.get(id)??-1,changed,[]);this.liveSyncPrompt();return{changed,removed:[],orderChanged:false,revision:snapshot.revision}},async liveRefreshCursor(signal=this.dataLifetime.signal){const query=gridQuery(this.view,{...this.acceptedQuery,cursor:undefined,revision:undefined,limit:this.pageLength});const page=validateGridCursorPage(await this.requestPage(query,signal),this.view,query,null);if(signal.aborted)throw new DOMException("The refresh was superseded.","AbortError");if(this.destroyed||page.revision===this.snapshot.revision||page.revision===this.livePending?.revision)return null;this.livePending={cursor:true,revision:page.revision};this.liveSyncPrompt();return{changed:new Map(),removed:[],orderChanged:false,newData:true,revision:page.revision}},liveSetHeld(changes,merge){const changed=new Map(),loaded=new Set(),pages=new Set(),query=this.acceptedQuery,used=new Set(query.sort.map(sort=>sort.column));const walk=group=>{if(!group)return;for(const filter of group.filters)used.add(filter.column);group.groups.forEach(walk)};for(const filter of query.filters)used.add(filter.column);walk(query.filterGroup);const searched=!!query.search;let moved=false;for(const row of this.rowCache.values()){const change=!row.status&&changes.get(row.key);if(!change)continue;loaded.add(row.key);const after=merge?liveMerged(row.h,change):liveCopy(change),columns=liveChangedColumns(row.h,after);if(!columns.size&&liveSameRow(row.h,after))continue;row.h=after;changed.set(row.key,columns);pages.add(Math.floor(row._ri / this.pageSize) * this.pageSize);
56
+ this.livePending=null;this.liveMoved=false;this.liveSwap(snapshot,()=>pages,id=>position.get(id)??-1,changed,[]);this.liveSyncPrompt();return{changed,removed:[],orderChanged:false,revision:snapshot.revision}},async liveRefreshCursor(signal=this.dataLifetime.signal){const query=gridQuery(this.view,{...this.acceptedQuery,cursor:undefined,revision:undefined,limit:this.pageLength});const page=validateGridCursorPage(await this.requestPage(query,signal),this.view,query,null);if(signal.aborted)throw new DOMException("The refresh was superseded.","AbortError");if(this.destroyed||page.revision===this.snapshot.revision||page.revision===this.livePending?.revision)return null;this.livePending={cursor:true,revision:page.revision};this.liveSyncPrompt();return{changed:new Map(),removed:[],orderChanged:false,newData:true,revision:page.revision}},liveSetHeld(changes,merge){const changed=new Map(),loaded=new Set(),pages=new Set(),query=this.acceptedQuery,used=new Set(query.sort.map(sort=>sort.column));const walk=group=>{if(!group)return;for(const filter of group.filters)used.add(filter.column);group.groups.forEach(walk)};for(const filter of query.filters)used.add(filter.column);walk(query.filterGroup);for(const tuple of query.tupleFilters||[])for(const field of tuple.columns)used.add(field.column);const searched=!!query.search;let moved=false;for(const row of this.rowCache.values()){const change=!row.status&&changes.get(row.key);if(!change)continue;loaded.add(row.key);const after=merge?liveMerged(row.h,change):liveCopy(change),columns=liveChangedColumns(row.h,after);if(!columns.size&&liveSameRow(row.h,after))continue;row.h=after;changed.set(row.key,columns);pages.add(Math.floor(row._ri / this.pageSize) * this.pageSize);
57
57
  if(searched||[...columns].some(id=>used.has(id)))moved=true}
58
58
  if(merge&&this.livePending?.pages){for(const items of this.livePending.pages.values())for(let index=0;index<items.length;index++){const item=items[index],change=loaded.has(item.id)&&changes.get(item.id);if(!change)continue;const after=liveMerged(item,change),columns=liveChangedColumns(item,after);items[index]=after;if(!liveSameRow(item,after)&&(searched||[...columns].some(id=>used.has(id))))moved=true}
59
59
  if(moved)this.livePending={refresh:true}}
@@ -5,21 +5,13 @@
5
5
  --mx-line:color-mix(in srgb,var(--hfx-color-border,light-dark(#e2e4e7,#272a30)) 70%,var(--hfx-color-surface,light-dark(#ffffff,#16181c)));--mx-none:color-mix(in srgb,var(--hfx-color-text-muted,light-dark(#626770,#8f949d)) 42%,var(--hfx-color-surface,light-dark(#ffffff,#16181c)));
6
6
  --mx-raised:color-mix(in srgb,var(--hfx-color-text,light-dark(#16181c,#f2f3f5)) 18%,transparent);}
7
7
  .cx-mx-bar{position:relative;display:flex;flex-wrap:wrap;align-items:center;gap:var(--cx-size-8px);}
8
- .cx-mx-input{box-sizing:border-box;height:var(--cx-size-32px);padding:0 var(--cx-size-10px);font:inherit;color:var(--hfx-color-text,light-dark(#16181c,#f2f3f5));background:var(--hfx-color-surface,light-dark(#ffffff,#16181c));
9
- border:var(--cx-size-1px) solid var(--hfx-color-border-control,light-dark(#8a8f98,#6e737c));border-radius:var(--hfx-radius-sm,4px);color-scheme:inherit;}
10
8
  .cx-mx-search{flex:1 1 var(--cx-size-180px);max-width:var(--cx-size-320px);}
11
- .cx-mx-button{box-sizing:border-box;min-height:var(--cx-size-32px);padding:0 var(--cx-size-12px);font:inherit;color:var(--hfx-color-text,light-dark(#16181c,#f2f3f5));background:var(--hfx-color-surface,light-dark(#ffffff,#16181c));
12
- border:var(--cx-size-1px) solid var(--hfx-color-border-control,light-dark(#8a8f98,#6e737c));border-radius:var(--hfx-radius-sm,4px);cursor:pointer;}
13
- .cx-mx-button:hover{background:var(--hfx-color-hover,light-dark(#f5f5f5,#232529));}
14
- .cx-mx-button.cx-mx-active{border-color:var(--hfx-color-accent,light-dark(#11776f,#37c9b8));color:var(--hfx-color-accent-text,light-dark(#02726a,#37c9b8));}
15
9
  .cx-mx-switch{display:inline-flex;align-items:center;gap:var(--cx-size-6px);min-height:var(--cx-size-24px);cursor:pointer;}
16
- .cx-mx-check{width:var(--cx-size-16px);height:var(--cx-size-16px);margin:0;accent-color:var(--hfx-color-accent,light-dark(#11776f,#37c9b8));}
17
10
  .cx-mx-summary{margin-inline-start:auto;color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));font-size:var(--cx-size-13px);font-variant-numeric:tabular-nums;}
18
11
  .cx-mx-panel{position:absolute;top:calc(100% + var(--cx-size-4px));inset-inline-start:0;z-index:30;box-sizing:border-box;width:min(var(--cx-size-340px),100%);max-height:var(--cx-size-60vh);
19
12
  overflow:auto;display:grid;gap:var(--cx-size-10px);padding:var(--cx-size-12px);background:var(--hfx-color-surface,light-dark(#ffffff,#16181c));border:var(--cx-size-1px) solid var(--hfx-color-border-strong,light-dark(#cdd0d5,#363a42));
20
13
  border-radius:var(--hfx-radius-control,var(--hfx-radius-md,6px));box-shadow:var(--hfx-elevation-2,0 1px 2px rgba(22,24,28,.05),0 6px 18px -10px rgba(22,24,28,.18));}
21
14
  .cx-mx-field{display:grid;gap:var(--cx-size-4px);font-size:var(--cx-size-13px);color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));}
22
- .cx-mx-field .cx-mx-input{color:var(--hfx-color-text,light-dark(#16181c,#f2f3f5));}
23
15
  .cx-mx-facet{display:grid;gap:var(--cx-size-4px);margin:0;padding:0;border:0;min-width:0;}
24
16
  .cx-mx-facet legend{padding:0 0 var(--cx-size-4px);font-size:var(--cx-size-13px);color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));}
25
17
  .cx-mx-keycount{color:var(--hfx-color-text-muted,light-dark(#626770,#8f949d));font-variant-numeric:tabular-nums;margin-inline-start:var(--cx-size-4px);}
@@ -31,6 +23,7 @@ border:var(--cx-size-1px) solid var(--mx-line);flex:none;}
31
23
  .cx-mx-swatch.cx-mx-none::after{content:"";width:var(--cx-size-3px);height:var(--cx-size-3px);border-radius:50%;background:var(--mx-none);}
32
24
  .cx-mx-scroll{position:relative;flex:0 1 auto;box-sizing:border-box;width:fit-content;max-width:100%;min-height:0;max-height:var(--cx-size-100vh);overflow:auto;background:var(--hfx-color-surface,light-dark(#ffffff,#16181c));border:var(--cx-size-1px) solid var(--hfx-color-border,light-dark(#e2e4e7,#272a30));border-radius:var(--hfx-radius-control,var(--hfx-radius-md,6px));
33
25
  overscroll-behavior:contain;scrollbar-color:var(--cx-scrollbar-thumb,auto) var(--cx-scrollbar-track,auto);}
26
+ [data-cx-chrome="embedded"] .cx-mx-scroll{border:0;border-radius:0;background:transparent;}
34
27
  .cx-mx-head{position:sticky;top:0;z-index:3;width:var(--mx-content-w);background:var(--hfx-color-surface-sunken,light-dark(#f7f8f9,#111317));}
35
28
  .cx-mx-head.cx-mx-raised{box-shadow:0 var(--cx-size-6px) var(--cx-size-8px) calc(var(--cx-size-6px) * -1) var(--mx-raised);}
36
29
  .cx-mx-band,.cx-mx-hrow,.cx-mx-frow{position:relative;display:flex;}
@@ -1,11 +1,11 @@
1
- (function(global){"use strict";const installed=new WeakSet();const part={version:"da5c61ff2cb9ebda",install(context){if(installed.has(context.MATRIX_IMPL))return;const{MATRIX_IMPL,h,setText,fill,plural,foldText,numberFormat,gridFreeze,toneOf,applyTone,toneColors,AxisWindow,PROTOCOL}=context;const MATRIX_LABELS={matrixRows:"Rows",matrixColumns:"Columns",matrixSearch:"Find rows and columns",matrixSortBy:"Sort by",matrixGroupBy:"Group by",matrixByLabel:"Name",matrixByTotal:"Total",matrixAsListed:"As listed",matrixNoGrouping:"No groups",matrixDescending:"Descending",matrixOnlyProblems:"Only problems",matrixHideEmpty:"Hide empty rows and columns",matrixSize:"{rows} × {columns}",matrixShown:"Rows: {rows} of {totalRows}, columns: {columns} of {totalColumns}",matrixAttention:"Need attention: {count}",matrixLegend:"States",matrixFilter:"Filter",matrixClearFilters:"Clear filters",matrixNoMatches:"No rows or columns match.",matrixSelf:"Same item",matrixNoData:"No data",matrixCell:"{row} and {column}: {state}",matrixTotal:"Total",matrixItemTotal:"{item}: {count}",matrixExpandGroup:"Expand {group}",matrixCollapseGroup:"Collapse {group}",matrixGroupCount:"{count}",matrixCellCount:"Cells: {count}",matrixDetails:"Details",matrixClose:"Close",matrixValue:"Value",matrixState:"State",matrixAllClear:"Nothing needs attention",matrixShowMore:"Show more"};function matrixLabels(...sources){const labels={...MATRIX_LABELS};for(const source of sources){if(source==null)continue;if(typeof source!=="object"||Array.isArray(source))matrixFail("labels must be an object of strings");for(const[name,text]of Object.entries(source)){if(!Object.hasOwn(labels,name)){if(/^matrix[A-Z]/.test(name)) matrixFail("there is no matrix label '" + name + "'"); continue; }
1
+ (function(global){"use strict";const installed=new WeakSet();const part={version:"e5fa5cb0660b953a",install(context){if(installed.has(context.MATRIX_IMPL))return;const{MATRIX_IMPL,h,setText,fill,plural,foldText,numberFormat,gridFreeze,toneOf,applyTone,toneColors,AxisWindow,PROTOCOL}=context;const MATRIX_LABELS={matrixRows:"Rows",matrixColumns:"Columns",matrixSearch:"Find rows and columns",matrixSortBy:"Sort by",matrixGroupBy:"Group by",matrixByLabel:"Name",matrixByTotal:"Total",matrixAsListed:"As listed",matrixNoGrouping:"No groups",matrixDescending:"Descending",matrixOnlyProblems:"Only problems",matrixHideEmpty:"Hide empty rows and columns",matrixSize:"{rows} × {columns}",matrixShown:"Rows: {rows} of {totalRows}, columns: {columns} of {totalColumns}",matrixAttention:"Need attention: {count}",matrixLegend:"States",matrixFilter:"Filter",matrixClearFilters:"Clear filters",matrixNoMatches:"No rows or columns match.",matrixSelf:"Same item",matrixNoData:"No data",matrixCell:"{row} and {column}: {state}",matrixTotal:"Total",matrixItemTotal:"{item}: {count}",matrixExpandGroup:"Expand {group}",matrixCollapseGroup:"Collapse {group}",matrixGroupCount:"{count}",matrixCellCount:"Cells: {count}",matrixDetails:"Details",matrixClose:"Close",matrixValue:"Value",matrixState:"State",matrixAllClear:"Nothing needs attention",matrixShowMore:"Show more"};function matrixLabels(...sources){const labels={...MATRIX_LABELS};for(const source of sources){if(source==null)continue;if(typeof source!=="object"||Array.isArray(source))matrixFail("labels must be an object of strings");for(const[name,text]of Object.entries(source)){if(!Object.hasOwn(labels,name)){if(/^matrix[A-Z]/.test(name)) matrixFail("there is no matrix label '" + name + "'"); continue; }
2
2
  if(typeof text!=="string"||!/\S/.test(text)) matrixFail("label '" + name + "' must be a string with visible text");
3
3
  labels[name]=text}}
4
4
  return Object.freeze(labels)}
5
5
  function matrixTemplate(text){const parts=text.split(/\{(\w+)\}/);
6
6
  return values=>{let result=parts[0];for(let i=1;i<parts.length;i +=2)result +=(values[parts[i]]??"{"+parts[i]+ "}")+ parts[i+1];return result}}
7
7
  function matrixFail(message){throw new Error("CanopyX: the matrix document is not valid: "+message+".")}
8
- const MATRIX_DIGITS="0123456789abcdefghijklmnopqrstuvwxyz",MATRIX_NONE=255,MATRIX_SELF=254;const MATRIX_SYMBOLS=["check","cross","warning","info","dot","dash","question","clock","pause"];const MATRIX_KEYS=["protocol","type","id","label","rows","columns","square","columnTitle","diagonal","states","cells","notes","totals","attentionOnly","hideEmpty","cellWidth","density","details","phone","legend","autoHeight","labels"];const matrixObject=value=>value!==null&&typeof value==="object"&&!Array.isArray(value);function matrixKeys(value,keys,what){if(!matrixObject(value))matrixFail(what+" must be an object");for(const key of Object.keys(value))if(!keys.includes(key))matrixFail(what+" has the unknown property '"+key+"'");return value}
8
+ const MATRIX_DIGITS="0123456789abcdefghijklmnopqrstuvwxyz",MATRIX_NONE=255,MATRIX_SELF=254;const MATRIX_SYMBOLS=["check","cross","warning","info","dot","dash","question","clock","pause"];const MATRIX_KEYS=["protocol","type","id","label","rows","columns","square","columnTitle","diagonal","states","cells","notes","totals","attentionOnly","hideEmpty","cellWidth","density","details","phone","legend","autoHeight","labels","timeSlider"];const matrixObject=value=>value!==null&&typeof value==="object"&&!Array.isArray(value);function matrixKeys(value,keys,what){if(!matrixObject(value))matrixFail(what+" must be an object");for(const key of Object.keys(value))if(!keys.includes(key))matrixFail(what+" has the unknown property '"+key+"'");return value}
9
9
  function matrixText(value,what,max,optional){if(value===undefined&&optional)return null;if(typeof value!=="string"||!/\S/.test(value) || value.length > max) matrixFail(what + " must be visible text of at most " + max + " characters");
10
10
  return value}
11
11
  function matrixId(value,what){if(typeof value!=="string"||!value.length||value.length>256||/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:^|[^\uD800-\uDBFF])[\uDC00-\uDFFF]/.test(value)) matrixFail(what + " must be a nonempty id of at most 256 characters");
@@ -71,7 +71,7 @@ fitNow(){const host=this.host,style=host.style;if(this.fitInline===undefined){co
71
71
  }
72
72
  if(this.doc.autoHeight===false||Number.isNaN(this.fitMax)){if(style.maxHeight!==this.fitInline)style.maxHeight=this.fitInline;return}
73
73
  if(!host.offsetHeight)return;const parts=[this.bar,this.legend,this.scroll,this.empty,this.cards].filter(part=>!part.hidden),hostStyle=getComputedStyle(host);let content=(parts.length-1)*(parseFloat(getComputedStyle(this.root).rowGap)||0);for(const part of parts){let height=part.getBoundingClientRect().height;if(part===this.scroll||part===this.cards)height=Math.min(height+part.scrollHeight-part.clientHeight,parseFloat(getComputedStyle(part).maxHeight)||Infinity);content +=height}
74
- const chrome=["paddingTop","paddingBottom","borderTopWidth","borderBottomWidth"].reduce((sum,name)=>sum +(parseFloat(hostStyle[name])||0),0);const value=Math.min(this.fitMax,Math.ceil(chrome+content))+ "px";if(style.maxHeight!==value)style.maxHeight=value}}
74
+ const chrome=["paddingTop","paddingBottom","borderTopWidth","borderBottomWidth"].reduce((sum,name)=>sum +(parseFloat(hostStyle[name])||0),0);for(const sibling of host.children)if(sibling!==this.root&&!sibling.hidden)content +=sibling.getBoundingClientRect().height;const value=Math.min(this.fitMax,Math.ceil(chrome+content))+ "px";if(style.maxHeight!==value)style.maxHeight=value}}
75
75
  Object.assign(MatrixView.prototype,{groupCount(axis,count){const template=(axis==="columns"?this.doc.columns:this.doc.rows).groupCount||this.labels.matrixGroupCount;return fill(plural(template,count,this.lang),{count:this.format.format(count)})},scheduleRender(){if(this.destroyed||this.frame)return;this.frame=requestAnimationFrame(()=>{this.frame=0;this.render()})},recycleAll(){this.lookCache=null;for(const row of this.rowsMounted.values())this.releaseRow(row);this.rowsMounted.clear();for(const map of[this.headMounted,this.footMounted,this.bandMounted]){for(const element of map.values())element.remove();map.clear()}},releaseRow(row){row.remove();row._key=null;if(this.rowPool.length<200)this.rowPool.push(row)},windows(){const scroll=this.scroll,viewH=Math.max(this.rowH,scroll.clientHeight-this.headH-this.bandH-this.footH),viewW=Math.max(this.cellW,scroll.clientWidth-this.rowHeadW-this.totalW);const rows=this.rowWindow.window(scroll.scrollTop,viewH,MATRIX_OVERSCAN),columns=this.columnWindow.window(Math.abs(scroll.scrollLeft),viewW,MATRIX_OVERSCAN);return{rows,columns,viewH,viewW}},render(){if(this.destroyed||this.scroll.hidden)return;const{rows,columns}=this.windows(),model=this.model,focus=this.focus,scroll=this.scroll,top=scroll.scrollTop;const below=scroll.scrollHeight-scroll.clientHeight-top>1;this.head.classList.toggle("cx-mx-raised",top>0);this.foot.classList.toggle("cx-mx-raised",below);const columnList=[];for(let c=columns.first;c<=columns.last;c++)columnList.push(c);if(focus.column>=0&&focus.column<model.columns.length&&(focus.column<columns.first||focus.column>columns.last))columnList.push(focus.column);const rowList=[];for(let r=rows.first;r<=rows.last;r++)rowList.push(r);if(focus.row>=0&&focus.row<model.rows.length&&(focus.row<rows.first||focus.row>rows.last))rowList.push(focus.row);this.paintHeader(columnList);this.paintBand(columns);this.placeBands();this.paintFoot(columnList);const keep=new Set();for(const r of rowList){const entry=model.rows[r];if(!entry)continue;keep.add(entry.key);let row=this.rowsMounted.get(entry.key);if(!row){row=this.rowPool.pop()||this.newRow();this.rowsMounted.set(entry.key,row);this.fillRow(row,entry,r);this.body.appendChild(row)}
76
76
  else if(row._index!==r)this.placeRow(row,r);this.drawRow(row,entry,columns.first,entry.kind==="heading"?-1:columns.last)}
77
77
  for(const[key,row]of this.rowsMounted)if(!keep.has(key)){this.releaseRow(row);this.rowsMounted.delete(key)}
@@ -113,27 +113,27 @@ event.preventDefault();this.moveTo(next[0],next[1])},onFocus(event){const elemen
113
113
  if(event.target.closest("a"))return;const target=this.cellAt(event);if(target){this.focus={row:target._r,column:target._c};this.render();const cell=this.focusElement(target._r,target._c);cell?.focus({preventScroll:true});this.openCell(cell||target)}},activate(element){if(!element)return;const toggle=element.querySelector(".cx-mx-toggle");if(toggle&&toggle._group){this.toggleGroup(toggle._group.axis,toggle._group.key);return}
114
114
  const link=element.querySelector("a[href]");if(link){link.click();return}
115
115
  if(element.classList.contains("cx-mx-cell")){this.openCell(element);return}
116
- const entry=element.classList.contains("cx-mx-hcell")&&this.model.columns[element._c];if(entry&&entry.band)this.toggleGroup("columns",entry.band.key)},toggleGroup(axis,key){const list=this.query.collapsed[axis],at=list.indexOf(key);if(at>=0)list.splice(at,1);else list.push(key);const entryKey="g"+key;this.derive(true);const index=(axis==="rows"?this.model.rows:this.model.columns).findIndex(entry=>entry.key===entryKey||entry.band&&entry.band.key===key);if(index>=0)this.moveTo(axis==="rows"?index:this.focus.row,axis==="rows"?-1:index);this.stateChanged()}});Object.assign(MatrixView.prototype,{buildBar(){const labels=this.labels,signal=this.lifetime.signal,bar=this.bar;const search=this.searchInput=h("input","cx-mx-input cx-mx-search");search.type="search";search.placeholder=labels.matrixSearch;search.setAttribute("aria-label",labels.matrixSearch);search.autocomplete="off";search.addEventListener("input",()=>{clearTimeout(this.searchTimer);this.searchTimer=setTimeout(()=>this.setQuery({search:search.value}),150)},{signal});this.axisButtons={};for(const axis of MATRIX_AXES){const button=h("button","cx-mx-button");button.type="button";button.setAttribute("aria-haspopup","dialog");button.setAttribute("aria-expanded","false");button.addEventListener("click",()=>this.togglePanel(axis),{signal});this.axisButtons[axis]=button}
117
- const toggle=this.attentionToggle=h("input","cx-mx-check"),switchLabel=h("label","cx-mx-switch"),switchText=h("span");toggle.type="checkbox";setText(switchText,labels.matrixOnlyProblems);switchLabel.append(toggle,switchText);toggle.addEventListener("change",()=>this.setQuery({attentionOnly:toggle.checked}),{signal});this.attentionSwitch=switchLabel;this.attentionText=switchText;const empty=this.emptyToggle=h("input","cx-mx-check"),emptyLabel=this.emptySwitch=h("label","cx-mx-switch"),emptyText=this.emptyText=h("span");empty.type="checkbox";setText(emptyText,labels.matrixHideEmpty);emptyLabel.append(empty,emptyText);empty.addEventListener("change",()=>this.setQuery({hideEmpty:empty.checked}),{signal});const clear=this.clearButton=h("button","cx-mx-button cx-mx-clear");clear.type="button";setText(clear,labels.matrixClearFilters);clear.addEventListener("click",()=>{search.value="";this.setQuery({search:"",filters:{rows:{},columns:{}},attentionOnly:false});search.focus()},{signal});this.summary=h("span","cx-mx-summary");this.summary.setAttribute("aria-live","off");this.panel=h("div","cx-mx-panel");this.panel.setAttribute("role","dialog");this.panel.hidden=true;document.addEventListener("keydown",event=>{if(event.key==="Escape"&&!this.panel.hidden&&!event.defaultPrevented){event.preventDefault();this.closePanel(true)}},{signal});document.addEventListener("pointerdown",event=>{if(!this.panel.hidden&&!this.panel.contains(event.target)&&!Object.values(this.axisButtons).includes(event.target.closest("button")))this.closePanel(false)},{signal});bar.append(search,this.axisButtons.rows,this.axisButtons.columns,switchLabel,emptyLabel,clear,this.summary,this.panel)},refreshBar(){const labels=this.labels;this.searchInput.placeholder=labels.matrixSearch;this.searchInput.setAttribute("aria-label",labels.matrixSearch);setText(this.attentionText,labels.matrixOnlyProblems);setText(this.clearButton,labels.matrixClearFilters);this.attentionSwitch.hidden=!this.doc.states.some(state=>state.attention);setText(this.emptyText,labels.matrixHideEmpty);this.emptySwitch.hidden=!this.doc.sparse},axisTitle(axis){return axis==="rows"?this.doc.rows.title||this.labels.matrixRows:this.doc.columnTitle||this.labels.matrixColumns},activeFilters(axis){return Object.values(this.query.filters[axis]).reduce((sum,values)=>sum +(values&&values.length?1:0),0)},syncBar(){const query=this.query;for(const axis of MATRIX_AXES){const count=this.activeFilters(axis),button=this.axisButtons[axis];setText(button,this.axisTitle(axis)+(count?" ("+count+")":""));button.classList.toggle("cx-mx-active",!!count||!!query.group[axis])}
116
+ const entry=element.classList.contains("cx-mx-hcell")&&this.model.columns[element._c];if(entry&&entry.band)this.toggleGroup("columns",entry.band.key)},toggleGroup(axis,key){const list=this.query.collapsed[axis],at=list.indexOf(key);if(at>=0)list.splice(at,1);else list.push(key);const entryKey="g"+key;this.derive(true);const index=(axis==="rows"?this.model.rows:this.model.columns).findIndex(entry=>entry.key===entryKey||entry.band&&entry.band.key===key);if(index>=0)this.moveTo(axis==="rows"?index:this.focus.row,axis==="rows"?-1:index);this.stateChanged()}});Object.assign(MatrixView.prototype,{buildBar(){const labels=this.labels,signal=this.lifetime.signal,bar=this.bar;const search=this.searchInput=h("input","cx-input cx-mx-input cx-mx-search");search.type="search";search.placeholder=labels.matrixSearch;search.setAttribute("aria-label",labels.matrixSearch);search.autocomplete="off";search.addEventListener("input",()=>{clearTimeout(this.searchTimer);this.searchTimer=setTimeout(()=>this.setQuery({search:search.value}),150)},{signal});this.axisButtons={};for(const axis of MATRIX_AXES){const button=h("button","cx-btn cx-mx-button");button.type="button";button.setAttribute("aria-haspopup","dialog");button.setAttribute("aria-expanded","false");button.addEventListener("click",()=>this.togglePanel(axis),{signal});this.axisButtons[axis]=button}
117
+ const toggle=this.attentionToggle=h("input","cx-check cx-mx-check"),switchLabel=h("label","cx-mx-switch"),switchText=h("span");toggle.type="checkbox";setText(switchText,labels.matrixOnlyProblems);switchLabel.append(toggle,switchText);toggle.addEventListener("change",()=>this.setQuery({attentionOnly:toggle.checked}),{signal});this.attentionSwitch=switchLabel;this.attentionText=switchText;const empty=this.emptyToggle=h("input","cx-check cx-mx-check"),emptyLabel=this.emptySwitch=h("label","cx-mx-switch"),emptyText=this.emptyText=h("span");empty.type="checkbox";setText(emptyText,labels.matrixHideEmpty);emptyLabel.append(empty,emptyText);empty.addEventListener("change",()=>this.setQuery({hideEmpty:empty.checked}),{signal});const clear=this.clearButton=h("button","cx-btn cx-mx-button cx-mx-clear");clear.type="button";setText(clear,labels.matrixClearFilters);clear.addEventListener("click",()=>{search.value="";this.setQuery({search:"",filters:{rows:{},columns:{}},attentionOnly:false});search.focus()},{signal});this.summary=h("span","cx-mx-summary");this.summary.setAttribute("aria-live","off");this.panel=h("div","cx-mx-panel");this.panel.setAttribute("role","dialog");this.panel.hidden=true;document.addEventListener("keydown",event=>{if(event.key==="Escape"&&!this.panel.hidden&&!event.defaultPrevented){event.preventDefault();this.closePanel(true)}},{signal});document.addEventListener("pointerdown",event=>{if(!this.panel.hidden&&!this.panel.contains(event.target)&&!Object.values(this.axisButtons).includes(event.target.closest("button")))this.closePanel(false)},{signal});bar.append(search,this.axisButtons.rows,this.axisButtons.columns,switchLabel,emptyLabel,clear,this.summary,this.panel)},refreshBar(){const labels=this.labels;this.searchInput.placeholder=labels.matrixSearch;this.searchInput.setAttribute("aria-label",labels.matrixSearch);setText(this.attentionText,labels.matrixOnlyProblems);setText(this.clearButton,labels.matrixClearFilters);this.attentionSwitch.hidden=!this.doc.states.some(state=>state.attention);setText(this.emptyText,labels.matrixHideEmpty);this.emptySwitch.hidden=!this.doc.sparse},axisTitle(axis){return axis==="rows"?this.doc.rows.title||this.labels.matrixRows:this.doc.columnTitle||this.labels.matrixColumns},activeFilters(axis){return Object.values(this.query.filters[axis]).reduce((sum,values)=>sum +(values&&values.length?1:0),0)},syncBar(){const query=this.query;for(const axis of MATRIX_AXES){const count=this.activeFilters(axis),button=this.axisButtons[axis];setText(button,this.axisTitle(axis)+(count?" ("+count+")":""));button.classList.toggle("active",!!count||!!query.group[axis])}
118
118
  this.attentionToggle.checked=query.attentionOnly;this.emptyToggle.checked=query.hideEmpty;if(this.searchInput.value!==query.search&&document.activeElement!==this.searchInput)this.searchInput.value=query.search;this.clearButton.hidden=!(query.search||query.attentionOnly||this.activeFilters("rows")||this.activeFilters("columns"));setText(this.summary,this.summaryText());if(!this.panel.hidden)this.fillPanel(this.panelAxis)},summaryText(){const labels=this.labels,model=this.model,doc=this.doc,f=value=>this.format.format(value);const filtered=model.visible.rows!==doc.rows.items.length||model.visible.columns!==doc.columns.items.length;const size=filtered?fill(labels.matrixShown,{rows:f(model.visible.rows),totalRows:f(doc.rows.items.length),columns:f(model.visible.columns),totalColumns:f(doc.columns.items.length)}):
119
119
  fill(labels.matrixSize,{rows:f(model.visible.rows),columns:f(model.visible.columns)});let attention=0;doc.states.forEach((state,index)=>{if(state.attention)attention +=model.counts[index]});return size +(doc.states.some(state=>state.attention)?" · "+fill(labels.matrixAttention,{count:f(attention)}):"")},paintLegend(){if(!this.doc.legend)return;const items=[],counts=this.model.counts;this.doc.states.forEach((state,index)=>{const item=h("span","cx-mx-key"),swatch=h("span","cx-mx-swatch" +(state.attention?" cx-mx-attn":"")),text=h("span"),count=h("span","cx-mx-keycount");item.setAttribute("role","listitem");applyTone(swatch,swatch.className,toneOf(state.tone));const symbol=matrixSymbol(state.symbol);if(symbol)swatch.appendChild(symbol);setText(text,state.label);setText(count,this.format.format(counts[index]));item.append(swatch,text,count);items.push(item)});const none=counts[this.doc.states.length];if(none){const item=h("span","cx-mx-key"),swatch=h("span","cx-mx-swatch cx-mx-none"),text=h("span"),count=h("span","cx-mx-keycount");item.setAttribute("role","listitem");setText(text,this.labels.matrixNoData);setText(count,this.format.format(none));item.append(swatch,text,count);items.push(item)}
120
120
  this.legend.replaceChildren(...items)},togglePanel(axis){if(!this.panel.hidden&&this.panelAxis===axis){this.closePanel(true);return}
121
- this.panelAxis=axis;this.fillPanel(axis);this.panel.hidden=false;for(const name of MATRIX_AXES)this.axisButtons[name].setAttribute("aria-expanded",String(name===axis));this.panel.querySelector("select,input")?.focus()},closePanel(returnFocus){if(this.panel.hidden)return;this.panel.hidden=true;for(const name of MATRIX_AXES)this.axisButtons[name].setAttribute("aria-expanded","false");if(returnFocus)this.axisButtons[this.panelAxis].focus()},fillPanel(axis){const labels=this.labels,doc=this.doc,data=doc[axis==="rows"?"rows":"columns"],query=this.query,panel=this.panel,signal=this.lifetime.signal;panel.setAttribute("aria-label",this.axisTitle(axis));const focused=panel.contains(document.activeElement)?document.activeElement.dataset.cxControl:null;const field=(text,control)=>{const label=h("label","cx-mx-field"),name=h("span");setText(name,text);label.append(name,control);return label};const select=(options,value,change,key)=>{const element=h("select","cx-mx-input");element.dataset.cxControl=key;for(const[optionValue,text]of options){const option=h("option");option.value=optionValue;setText(option,text);element.appendChild(option)}
122
- element.value=value;element.addEventListener("change",()=>change(element.value),{signal});return element};const sort=query.sort[axis];const sortBy=select([["",labels.matrixAsListed],["#label",labels.matrixByLabel],["#total",labels.matrixByTotal],...data.attributes.map(attribute=>[attribute.id,attribute.title])],sort?sort.by:"",value=>this.sort(axis,value||null,value==="#total"?"desc":query.sort[axis]?.direction||"asc"),"sort");const descending=h("input","cx-mx-check");descending.type="checkbox";descending.checked=!!sort&&sort.direction==="desc";descending.disabled=!sort;descending.dataset.cxControl="direction";descending.addEventListener("change",()=>this.sort(axis,query.sort[axis]?.by||null,descending.checked?"desc":"asc"),{signal});const descendingLabel=h("label","cx-mx-switch"),descendingText=h("span");setText(descendingText,labels.matrixDescending);descendingLabel.append(descending,descendingText);const groupBy=select([["",labels.matrixNoGrouping],...data.attributes.map(attribute=>[attribute.id,attribute.title])],query.group[axis]||"",value=>this.group(axis,value||null),"group");const children=[field(labels.matrixSortBy,sortBy),descendingLabel];if(data.attributes.length)children.push(field(labels.matrixGroupBy,groupBy));for(const attribute of data.attributes.filter(item=>item.facet))children.push(this.facetList(axis,data,attribute));const close=h("button","cx-mx-button");close.type="button";setText(close,labels.matrixClose);close.dataset.cxControl="close";close.addEventListener("click",()=>this.closePanel(true),{signal});children.push(close);panel.replaceChildren(...children);if(focused)panel.querySelector('[data-cx-control="'+CSS.escape(focused)+ '"]')?.focus()},facetList(axis,data,attribute){const set=h("fieldset","cx-mx-facet"),legend=h("legend"),counts=new Map(),chosen=new Set(this.query.filters[axis][attribute.id]||[]),signal=this.lifetime.signal;setText(legend,attribute.title);set.appendChild(legend);for(const item of data.items){const key=matrixValueKey(item.values[attribute.id]);if(!counts.has(key))counts.set(key,{key,text:matrixValueText(item.values[attribute.id],attribute,this.format)||"—",count:0});counts.get(key).count++}
123
- for(const value of[...counts.values()].sort((a,b)=>matrixNatural(a.text,b.text)).slice(0,200)){const label=h("label","cx-mx-switch"),box=h("input","cx-mx-check"),text=h("span"),count=h("span","cx-mx-keycount");box.type="checkbox";box.checked=chosen.has(value.key);box.dataset.cxControl="facet:"+attribute.id+":"+value.key;box.addEventListener("change",()=>{const next=new Set(this.query.filters[axis][attribute.id]||[]);if(box.checked)next.add(value.key);else next.delete(value.key);this.filter(axis,attribute.id,[...next])},{signal});setText(text,value.text);setText(count,this.format.format(value.count));label.append(box,text,count);set.appendChild(label)}
121
+ this.panelAxis=axis;this.fillPanel(axis);this.panel.hidden=false;for(const name of MATRIX_AXES)this.axisButtons[name].setAttribute("aria-expanded",String(name===axis));this.panel.querySelector("select,input")?.focus()},closePanel(returnFocus){if(this.panel.hidden)return;this.panel.hidden=true;for(const name of MATRIX_AXES)this.axisButtons[name].setAttribute("aria-expanded","false");if(returnFocus)this.axisButtons[this.panelAxis].focus()},fillPanel(axis){const labels=this.labels,doc=this.doc,data=doc[axis==="rows"?"rows":"columns"],query=this.query,panel=this.panel,signal=this.lifetime.signal;panel.setAttribute("aria-label",this.axisTitle(axis));const focused=panel.contains(document.activeElement)?document.activeElement.dataset.cxControl:null;const field=(text,control)=>{const label=h("label","cx-mx-field"),name=h("span");setText(name,text);label.append(name,control);return label};const select=(options,value,change,key)=>{const element=h("select","cx-select cx-mx-input");element.dataset.cxControl=key;for(const[optionValue,text]of options){const option=h("option");option.value=optionValue;setText(option,text);element.appendChild(option)}
122
+ element.value=value;element.addEventListener("change",()=>change(element.value),{signal});return element};const sort=query.sort[axis];const sortBy=select([["",labels.matrixAsListed],["#label",labels.matrixByLabel],["#total",labels.matrixByTotal],...data.attributes.map(attribute=>[attribute.id,attribute.title])],sort?sort.by:"",value=>this.sort(axis,value||null,value==="#total"?"desc":query.sort[axis]?.direction||"asc"),"sort");const descending=h("input","cx-mx-check");descending.type="checkbox";descending.checked=!!sort&&sort.direction==="desc";descending.disabled=!sort;descending.dataset.cxControl="direction";descending.addEventListener("change",()=>this.sort(axis,query.sort[axis]?.by||null,descending.checked?"desc":"asc"),{signal});const descendingLabel=h("label","cx-mx-switch"),descendingText=h("span");setText(descendingText,labels.matrixDescending);descendingLabel.append(descending,descendingText);const groupBy=select([["",labels.matrixNoGrouping],...data.attributes.map(attribute=>[attribute.id,attribute.title])],query.group[axis]||"",value=>this.group(axis,value||null),"group");const children=[field(labels.matrixSortBy,sortBy),descendingLabel];if(data.attributes.length)children.push(field(labels.matrixGroupBy,groupBy));for(const attribute of data.attributes.filter(item=>item.facet))children.push(this.facetList(axis,data,attribute));const close=h("button","cx-btn cx-mx-button");close.type="button";setText(close,labels.matrixClose);close.dataset.cxControl="close";close.addEventListener("click",()=>this.closePanel(true),{signal});children.push(close);panel.replaceChildren(...children);if(focused)panel.querySelector('[data-cx-control="'+CSS.escape(focused)+ '"]')?.focus()},facetList(axis,data,attribute){const set=h("fieldset","cx-mx-facet"),legend=h("legend"),counts=new Map(),chosen=new Set(this.query.filters[axis][attribute.id]||[]),signal=this.lifetime.signal;setText(legend,attribute.title);set.appendChild(legend);for(const item of data.items){const key=matrixValueKey(item.values[attribute.id]);if(!counts.has(key))counts.set(key,{key,text:matrixValueText(item.values[attribute.id],attribute,this.format)||"—",count:0});counts.get(key).count++}
123
+ for(const value of[...counts.values()].sort((a,b)=>matrixNatural(a.text,b.text)).slice(0,200)){const label=h("label","cx-mx-switch"),box=h("input","cx-check cx-mx-check"),text=h("span"),count=h("span","cx-mx-keycount");box.type="checkbox";box.checked=chosen.has(value.key);box.dataset.cxControl="facet:"+attribute.id+":"+value.key;box.addEventListener("change",()=>{const next=new Set(this.query.filters[axis][attribute.id]||[]);if(box.checked)next.add(value.key);else next.delete(value.key);this.filter(axis,attribute.id,[...next])},{signal});setText(text,value.text);setText(count,this.format.format(value.count));label.append(box,text,count);set.appendChild(label)}
124
124
  return set}});Object.assign(MatrixView.prototype,{cellInfo(cell){const row=this.model.rows[cell._r],column=this.model.columns[cell._c];if(!row||!column)return null;const info=this.model.cell(row,column),state=info.state<MATRIX_SELF?this.doc.states[info.state]:null;return{row,column,info,state}},cellLines(data){const labels=this.labels,lines=[],note=data.info.note;const stateText=data.state?data.state.label:data.info.state===MATRIX_SELF?labels.matrixSelf:labels.matrixNoData;lines.push([labels.matrixState,stateText]);if(note&&note.value!=null)lines.push([labels.matrixValue,this.format.format(note.value)]);if(note&&note.text)lines.push([note.value!=null?"":labels.matrixValue,note.text]);if(data.info.rolled){lines.push(["",fill(labels.matrixCellCount,{count:this.format.format(data.info.members)})]);if(data.info.attention)lines.push(["",fill(labels.matrixAttention,{count:this.format.format(data.info.attention)})])}
125
125
  if(note&&note.title)lines.push(["",note.title]);for(const[label,value]of note&&note.details||[])lines.push([label,value]);return lines},onPointer(event){const target=this.cellAt(event);if(target&&this.pointerCell&&target._r===this.pointerCell._r&&target._c===this.pointerCell._c)return;this.pointerCell=target;this.hoverCell(target);clearTimeout(this.tipTimer);if(!target){if(!this.tipKeyboard)this.hideTip();return}
126
126
  this.tipTimer=setTimeout(()=>{if(!this.destroyed&&this.pointerCell===target)this.showTip(target,false)},250)},showTip(cell,keyboard){const data=this.cellInfo(cell);if(!data)return;this.tipKeyboard=keyboard;const tip=this.tip,title=h("div","cx-mx-tiptitle"),list=h("dl","cx-mx-tiplist");setText(title,data.row.label+" · "+data.column.label);for(const[label,value]of this.cellLines(data).slice(0,8)){const term=h("dt"),text=h("dd");setText(term,label);setText(text,value);list.append(term,text)}
127
127
  tip.replaceChildren(title,list);tip.hidden=false;const box=this.cellRect(cell._r,cell._c),frame=this.root.getBoundingClientRect(),width=tip.offsetWidth,height=tip.offsetHeight;if(!box){this.hideTip();return}
128
128
  let left=box.left-frame.left+box.width / 2 - width / 2, top = box.bottom - frame.top + 6;
129
129
  left=Math.max(4,Math.min(left,frame.width-width-4));if(box.bottom+height+10>global.innerHeight&&box.top-height-6>0)top=box.top-frame.top-height-6;tip.style.left=left+"px";tip.style.top=top+"px"},hideTip(){clearTimeout(this.tipTimer);this.tip.hidden=true;this.tipKeyboard=false;this.pointerCell=null;this.hoverElement?.remove()},openCell(cell){const data=this.cellInfo(cell);if(!data)return;this.hideTip();const note=data.info.note,member=entry=>entry.kind==="item"?entry.item.id:null,group=entry=>entry.kind==="item"?null:entry.group.key;const detail=Object.freeze({matrix:this.doc.id,row:member(data.row),rowGroup:group(data.row),column:member(data.column),columnGroup:group(data.column),state:data.state?data.state.id:data.info.state===MATRIX_SELF?"self":null,value:note?note.value:null,text:note?note.text:null,title:note?note.title:null,details:note&&note.details?note.details.map(pair=>[...pair]):[],members:data.info.members,attention:data.info.attention});if(!this.emit("matrixCell",detail,"onCell",true)||this.doc.details==="host")return;this.openDialog(data,cell)},openDialog(data,cell){const labels=this.labels,dialog=this.dialog||=this.buildDialog();setText(dialog._title,data.row.label+" · "+data.column.label);const rows=[];for(const[label,value]of this.cellLines(data)){const term=h("dt"),text=h("dd");setText(term,label);setText(text,value);rows.push(term,text)}
130
- dialog._list.replaceChildren(...rows);const state=data.state,badge=dialog._state;badge.replaceChildren();applyTone(badge,"cx-mx-swatch" +(state&&state.attention?" cx-mx-attn":""),toneOf(state?state.tone:null));const symbol=state&&matrixSymbol(state.symbol);if(symbol)badge.appendChild(symbol);setText(dialog._close,labels.matrixClose);if(typeof dialog.showModal==="function")dialog.showModal();else dialog.setAttribute("open","");dialog._close.focus()},buildDialog(){const dialog=h("dialog","cx-mx-dialog"),head=h("div","cx-mx-dialoghead"),title=h("h2","cx-mx-dialogtitle"),state=h("span","cx-mx-swatch"),list=h("dl","cx-mx-details");const close=h("button","cx-mx-button"),signal=this.lifetime.signal;title.id=(this.host.id||"cx-matrix")+ "-"+Math.random().toString(36).slice(2)+ "-title";dialog.setAttribute("aria-labelledby",title.id);close.type="button";close.addEventListener("click",()=>dialog.close?dialog.close():dialog.removeAttribute("open"),{signal});dialog.addEventListener("close",()=>{if(!this.destroyed)this.moveTo(this.focus.row,this.focus.column,true,false)},{signal});head.append(state,title);dialog.append(head,list,close);dialog._title=title;dialog._list=list;dialog._close=close;dialog._state=state;this.root.appendChild(dialog);return dialog}});const MATRIX_GLYPHS={check:"✓",cross:"✕",warning:"!",info:"i",dot:"•",dash:"–",question:"?",clock:"◷",pause:"‖"};Object.assign(MatrixView.prototype,{attentionCells(row){const cells=[];for(const column of this.model.columns){const info=this.model.cell(row,column);if(info.state<MATRIX_SELF&&this.doc.attention[info.state])cells.push({column,info,state:this.doc.states[info.state]})}
130
+ dialog._list.replaceChildren(...rows);const state=data.state,badge=dialog._state;badge.replaceChildren();applyTone(badge,"cx-mx-swatch" +(state&&state.attention?" cx-mx-attn":""),toneOf(state?state.tone:null));const symbol=state&&matrixSymbol(state.symbol);if(symbol)badge.appendChild(symbol);setText(dialog._close,labels.matrixClose);if(typeof dialog.showModal==="function")dialog.showModal();else dialog.setAttribute("open","");dialog._close.focus()},buildDialog(){const dialog=h("dialog","cx-mx-dialog"),head=h("div","cx-mx-dialoghead"),title=h("h2","cx-mx-dialogtitle"),state=h("span","cx-mx-swatch"),list=h("dl","cx-mx-details");const close=h("button","cx-btn cx-mx-button"),signal=this.lifetime.signal;title.id=(this.host.id||"cx-matrix")+ "-"+Math.random().toString(36).slice(2)+ "-title";dialog.setAttribute("aria-labelledby",title.id);close.type="button";close.addEventListener("click",()=>dialog.close?dialog.close():dialog.removeAttribute("open"),{signal});dialog.addEventListener("close",()=>{if(!this.destroyed)this.moveTo(this.focus.row,this.focus.column,true,false)},{signal});head.append(state,title);dialog.append(head,list,close);dialog._title=title;dialog._list=list;dialog._close=close;dialog._state=state;this.root.appendChild(dialog);return dialog}});const MATRIX_GLYPHS={check:"✓",cross:"✕",warning:"!",info:"i",dot:"•",dash:"–",question:"?",clock:"◷",pause:"‖"};Object.assign(MatrixView.prototype,{attentionCells(row){const cells=[];for(const column of this.model.columns){const info=this.model.cell(row,column);if(info.state<MATRIX_SELF&&this.doc.attention[info.state])cells.push({column,info,state:this.doc.states[info.state]})}
131
131
  return cells},renderCards(){const labels=this.labels,list=h("ul","cx-mx-cardlist"),limit=this.cardLimit||=100,rows=this.model.rows.filter(entry=>entry.kind!=="heading");let group=null;for(const entry of rows.slice(0,limit)){if(entry.band&&entry.band!==group){group=entry.band;const heading=h("li","cx-mx-cardgroup");setText(heading,group.label);list.appendChild(heading)}
132
132
  const card=h("li","cx-mx-card"),head=h("div","cx-mx-cardhead"),title=h("span","cx-mx-label"),total=h("span","cx-mx-total" +(entry.total?"":" cx-mx-zero"));setText(title,entry.label);setText(total,this.format.format(entry.total||0));total.setAttribute("aria-label",fill(labels.matrixItemTotal,{item:entry.label,count:this.format.format(entry.total||0)}));head.append(title,total);card.appendChild(head);if(entry.kind==="item"&&entry.item.secondary){const sub=h("div","cx-mx-sub");setText(sub,entry.item.secondary);card.appendChild(sub)}
133
133
  const cells=this.attentionCells(entry),items=h("ul","cx-mx-cardcells");for(const{column,info,state}of cells.slice(0,12)){const item=h("li","cx-mx-cardcell"),swatch=h("span","cx-mx-swatch cx-mx-attn"),text=h("span");applyTone(swatch,swatch.className,toneOf(state.tone));const symbol=matrixSymbol(state.symbol);if(symbol)swatch.appendChild(symbol);const note=info.note,extra=note&&note.text?note.text:note&&note.value!=null?this.format.format(note.value):"";setText(text,column.label+" · "+state.label +(extra?" · "+extra:""));item.append(swatch,text);items.appendChild(item)}
134
134
  if(cells.length>12){const more=h("li","cx-mx-sub");setText(more,"+"+this.format.format(cells.length-12));items.appendChild(more)}
135
135
  if(!cells.length){const clear=h("div","cx-mx-sub cx-mx-clear-text");setText(clear,labels.matrixAllClear);card.appendChild(clear)}else card.appendChild(items);list.appendChild(card)}
136
- const children=[list];if(rows.length>limit){const more=h("button","cx-mx-button");more.type="button";setText(more,labels.matrixShowMore);more.addEventListener("click",()=>{this.cardLimit +=100;this.renderCards()},{signal:this.lifetime.signal});children.push(more)}
136
+ const children=[list];if(rows.length>limit){const more=h("button","cx-btn cx-mx-button");more.type="button";setText(more,labels.matrixShowMore);more.addEventListener("click",()=>{this.cardLimit +=100;this.renderCards()},{signal:this.lifetime.signal});children.push(more)}
137
137
  this.cards.replaceChildren(...children)},stateGlyph(info){if(info.state===MATRIX_SELF)return "";if(info.state===MATRIX_NONE)return "";const state=this.doc.states[info.state];return(MATRIX_GLYPHS[state.symbol]||"")+(info.note&&info.note.text?" "+info.note.text:info.note&&info.note.value!=null?" "+this.format.format(info.note.value):"")},printCell(info){const td=h("td"),state=info.state<MATRIX_SELF?this.doc.states[info.state]:null,labels=this.labels;setText(td,info.state===MATRIX_NONE?"·":this.stateGlyph(info)+(info.rolled&&info.attention?" "+this.format.format(info.attention):""));td.title=state?state.label:info.state===MATRIX_SELF?labels.matrixSelf:labels.matrixNoData;if(state)applyTone(td,"cx-mx-printcell" +(state.attention?" cx-mx-attn":""),toneOf(state.tone));else td.className=info.state===MATRIX_SELF?"cx-mx-printself":"cx-mx-printnone";return td},printHead(table,columns){const head=h("thead"),colgroup=span=>{const group=h("colgroup");group.span=span;table.appendChild(group)};if(columns.some(entry=>entry.band)){const band=h("tr");band.appendChild(h("td"));colgroup(1);for(let c=0;c<columns.length;){const group=columns[c].band,th=h(group?"th":"td");let span=1;if(group){while(c+span<columns.length&&columns[c+span].band===group)span++;const count=h("span","cx-mx-printcount");th.scope="colgroup";setText(th,group.label);setText(count," · "+this.groupCount("columns",group.members.length));th.appendChild(count)}
138
138
  th.colSpan=span;band.appendChild(th);colgroup(span);c +=span}
139
139
  if(this.totalW){band.appendChild(h("td"));colgroup(1)}
@@ -157,4 +157,4 @@ if(state.attentionOnly!==undefined)next.attentionOnly=!!state.attentionOnly;if(s
157
157
  if(state.collapsed&&state.collapsed[axis]!==undefined){if(!Array.isArray(state.collapsed[axis])||state.collapsed[axis].some(key=>typeof key!=="string"))throw new Error("CanopyX: collapsed groups are a list of group keys.");next.collapsed[axis]=[...new Set(state.collapsed[axis])]}}
158
158
  this.query=next;if(derive){this.derive(true);this.stateChanged()}},setQuery(partial){this.applyState(partial)},sort(axis,by,direction="asc"){this.applyState({sort:{[matrixAxisName(axis)]:by==null?null:{by,direction}}})},group(axis,attribute){this.applyState({group:{[matrixAxisName(axis)]:attribute??null}})},filter(axis,attribute,values){this.applyState({filters:{[matrixAxisName(axis)]:{...this.query.filters[axis],[attribute]:values??null}}})},getState(){const query=this.query,copy=value=>JSON.parse(JSON.stringify(value));return gridFreeze({search:query.search,attentionOnly:query.attentionOnly,hideEmpty:query.hideEmpty,filters:copy(query.filters),sort:copy(query.sort),group:copy(query.group),collapsed:copy(query.collapsed)})},stateChanged(){this.emit("matrixState",this.getState(),"onStateChange")},getVisible(){const ids=entries=>entries.filter(entry=>entry.kind!=="heading").flatMap(entry=>entry.members.map(item=>item.id));return gridFreeze({rows:ids(this.model.rows),columns:ids(this.model.columns)})},focusCell(row,column){const r=this.model.rows.findIndex(entry=>entry.kind!=="heading"&&entry.members.some(item=>item.id===row));const c=this.model.columns.findIndex(entry=>entry.members.some(item=>item.id===column));if(r<0||c<0)return false;this.moveTo(r,c);return true},update(view){const doc=matrixDocument(view),state=JSON.parse(JSON.stringify(this.getState())),focus=this.captureFocus();this.doc=doc;this.labels=matrixLabels(doc.labels,this.options.labels);this.model=new MatrixModel(doc,this.format);this.query=this.model.initialQuery();for(const axis of MATRIX_AXES){const attributes=this.axisData(axis).attributeById;state.filters[axis]=Object.fromEntries(Object.entries(state.filters[axis]).filter(([id])=>attributes.has(id)));const sort=state.sort[axis];if(sort&&!["#label","#total"].includes(sort.by)&&!attributes.has(sort.by))state.sort[axis]=null;if(state.group[axis]!==null&&!attributes.has(state.group[axis])){state.group[axis]=null;state.collapsed[axis]=[]}}
159
159
  this.applyState(state,false);this.root.className="cx-matrix cx-mx-"+doc.density;this.source=view;this.closePanel(false);this.refreshChrome();this.geometry=undefined;this.derive(false,focus)}});function matrixCreate(host,options){const view=new MatrixView(host,options);const impl={ready:Promise.resolve().then(()=>{if(view.destroyed)return impl;host.setAttribute("data-cx-ready","");view.emit("matrixReady",{matrix:view.doc.id},"onReady");return impl}),document:()=>view.source||view.options.view,sort:(axis,by,direction)=>view.sort(axis,by,direction),group:(axis,attribute)=>view.group(axis,attribute),filter:(axis,attribute,values)=>view.filter(axis,attribute,values),setSearch:text=>view.setQuery({search:text}),setAttentionOnly:on=>view.setQuery({attentionOnly:!!on}),getState:()=>view.getState(),setState:state=>view.applyState(state),getVisible:()=>view.getVisible(),focusCell:(row,column)=>view.focusCell(row,column),update:next=>view.update(next),destroy:()=>{if(view.destroyed)return;view.destroyed=true;view.lifetime.abort();view.resize?.disconnect();view.themeWatch?.disconnect();cancelAnimationFrame(view.frame);cancelAnimationFrame(view.fitFrame);clearTimeout(view.searchTimer);clearTimeout(view.tipTimer);view.root.remove();host.removeAttribute("data-cx-ready");if(view.fitInline!==undefined){host.style.maxHeight=view.fitInline;if(!host.getAttribute("style"))host.removeAttribute("style")}}};return impl}
160
- MATRIX_IMPL.create=matrixCreate;installed.add(context.MATRIX_IMPL)}};(global.CanopyXParts||=[]).push(["matrix",part])})(typeof window!=="undefined"?window:globalThis);
160
+ Object.assign(MATRIX_IMPL,{create:matrixCreate,validate:matrixDocument});installed.add(context.MATRIX_IMPL)}};(global.CanopyXParts||=[]).push(["matrix",part])})(typeof window!=="undefined"?window:globalThis);