@evotecit/canopyx 0.1.3 → 0.2.1

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 (85) hide show
  1. package/CanopyX.Html/Assets/canopyx-calendar-edit.js +114 -0
  2. package/CanopyX.Html/Assets/canopyx-calendar-ics.js +87 -0
  3. package/CanopyX.Html/Assets/canopyx-calendar-recurrence.js +36 -0
  4. package/CanopyX.Html/Assets/canopyx-calendar.css +279 -0
  5. package/CanopyX.Html/Assets/canopyx-calendar.js +307 -0
  6. package/CanopyX.Html/Assets/canopyx-cell-range.css +38 -0
  7. package/CanopyX.Html/Assets/canopyx-cell-range.js +141 -0
  8. package/CanopyX.Html/Assets/canopyx-column-groups.css +18 -0
  9. package/CanopyX.Html/Assets/canopyx-column-groups.js +51 -0
  10. package/CanopyX.Html/Assets/canopyx-data.js +14 -0
  11. package/CanopyX.Html/Assets/canopyx-detail-rows.css +18 -0
  12. package/CanopyX.Html/Assets/canopyx-detail-rows.js +51 -0
  13. package/CanopyX.Html/Assets/canopyx-editing.css +44 -0
  14. package/CanopyX.Html/Assets/canopyx-editing.js +134 -0
  15. package/CanopyX.Html/Assets/canopyx-enhance.js +126 -0
  16. package/CanopyX.Html/Assets/canopyx-export.js +18 -0
  17. package/CanopyX.Html/Assets/canopyx-facets.css +86 -0
  18. package/CanopyX.Html/Assets/canopyx-facets.js +65 -0
  19. package/CanopyX.Html/Assets/canopyx-filter-editor.css +15 -18
  20. package/CanopyX.Html/Assets/canopyx-filter-editor.js +23 -17
  21. package/CanopyX.Html/Assets/canopyx-format-editor.css +77 -0
  22. package/CanopyX.Html/Assets/canopyx-format-editor.js +30 -0
  23. package/CanopyX.Html/Assets/canopyx-formatting.css +40 -0
  24. package/CanopyX.Html/Assets/canopyx-formatting.js +101 -0
  25. package/CanopyX.Html/Assets/canopyx-grid-module.mjs +352 -246
  26. package/CanopyX.Html/Assets/canopyx-grid.css +139 -117
  27. package/CanopyX.Html/Assets/canopyx-grid.js +352 -246
  28. package/CanopyX.Html/Assets/canopyx-grouping.css +39 -0
  29. package/CanopyX.Html/Assets/canopyx-grouping.js +85 -0
  30. package/CanopyX.Html/Assets/canopyx-highlight.css +34 -0
  31. package/CanopyX.Html/Assets/canopyx-highlight.js +80 -0
  32. package/CanopyX.Html/Assets/canopyx-history.js +13 -0
  33. package/CanopyX.Html/Assets/canopyx-interactive.css +18 -6
  34. package/CanopyX.Html/Assets/canopyx-interactive.js +179 -100
  35. package/CanopyX.Html/Assets/canopyx-labels-de.js +304 -0
  36. package/CanopyX.Html/Assets/canopyx-labels-es.js +304 -0
  37. package/CanopyX.Html/Assets/canopyx-labels-fr.js +529 -0
  38. package/CanopyX.Html/Assets/canopyx-labels-it.js +529 -0
  39. package/CanopyX.Html/Assets/canopyx-labels-pl.js +304 -0
  40. package/CanopyX.Html/Assets/canopyx-layouts.css +48 -0
  41. package/CanopyX.Html/Assets/canopyx-layouts.js +39 -0
  42. package/CanopyX.Html/Assets/canopyx-link.css +19 -0
  43. package/CanopyX.Html/Assets/canopyx-link.js +38 -0
  44. package/CanopyX.Html/Assets/canopyx-links.js +14 -0
  45. package/CanopyX.Html/Assets/canopyx-live.css +24 -0
  46. package/CanopyX.Html/Assets/canopyx-live.js +88 -0
  47. package/CanopyX.Html/Assets/canopyx-module.mjs +180 -100
  48. package/CanopyX.Html/Assets/canopyx-pager.css +10 -0
  49. package/CanopyX.Html/Assets/canopyx-pager.js +14 -0
  50. package/CanopyX.Html/Assets/canopyx-record-panel.css +111 -0
  51. package/CanopyX.Html/Assets/canopyx-record-panel.js +178 -0
  52. package/CanopyX.Html/Assets/canopyx-reporting-module.mjs +406 -342
  53. package/CanopyX.Html/Assets/canopyx-reporting.css +140 -135
  54. package/CanopyX.Html/Assets/canopyx-reporting.js +406 -342
  55. package/CanopyX.Html/Assets/canopyx-search.js +32 -0
  56. package/CanopyX.Html/Assets/canopyx-timeline.css +80 -0
  57. package/CanopyX.Html/Assets/canopyx-timeline.js +88 -0
  58. package/CanopyX.Html/Assets/canopyx-tree-tools.css +103 -0
  59. package/CanopyX.Html/Assets/canopyx-tree-tools.js +92 -0
  60. package/CanopyX.Html/Assets/canopyx.css +17 -5
  61. package/CanopyX.Html/Assets/canopyx.js +175 -96
  62. package/JavaScript/calendar.d.ts +362 -0
  63. package/JavaScript/cell-ranges.d.ts +76 -0
  64. package/JavaScript/column-groups.d.ts +32 -0
  65. package/JavaScript/editing.d.ts +257 -0
  66. package/JavaScript/enhance.d.ts +202 -0
  67. package/JavaScript/facets.d.ts +58 -0
  68. package/JavaScript/formatting.d.ts +88 -0
  69. package/JavaScript/grid.d.ts +409 -56
  70. package/JavaScript/hosts.d.ts +177 -2
  71. package/JavaScript/index.d.ts +53 -4
  72. package/JavaScript/live.d.ts +115 -0
  73. package/JavaScript/locales/de.mjs +304 -0
  74. package/JavaScript/locales/es.mjs +304 -0
  75. package/JavaScript/locales/fr.d.ts +6 -0
  76. package/JavaScript/locales/fr.mjs +527 -0
  77. package/JavaScript/locales/it.d.ts +6 -0
  78. package/JavaScript/locales/it.mjs +527 -0
  79. package/JavaScript/locales/pl.mjs +304 -0
  80. package/JavaScript/plugins.d.ts +113 -0
  81. package/JavaScript/protocol.d.ts +139 -6
  82. package/JavaScript/records.d.ts +136 -0
  83. package/README.md +61 -9
  84. package/README.nuget.md +1 -1
  85. package/package.json +99 -3
@@ -3,6 +3,7 @@
3
3
  const PROTOCOL="canopyx/2";const OVERSCAN=8;const TYPEAHEAD_MS=600;const INDENT=18;const COUNTS_BATCH=50;const SHARD_CACHE=24;const ENTRY_CACHE=32;const ENTRY_BATCH=8;const PACK_CACHE=6;const PACK_CACHE_BYTES=2*1024*1024;const DATE_CACHE=4096;const SEARCH_CANDIDATES=5;const MAX_PROTOCOL_INTEGER=2147483647;const MAX_COLUMN_WIDTH=MAX_PROTOCOL_INTEGER;const activeGrids=new WeakMap();let instanceSeq=0;function h(tag,className){const element=document.createElement(tag);if(className){element.className=className}
4
4
  return element}
5
5
  function setText(element,value){element.textContent=value==null?"":String(value)}
6
+ function filterChipContent(button,text){const label=h("span","cx-chip-label"),remove=h("span","cx-chip-remove");setText(label,text);setText(remove," ×");remove.setAttribute("aria-hidden","true");button.classList.add("cx-filter-chip");button.replaceChildren(label,remove)}
6
7
  function validStructuralCount(value){return Number.isInteger(value)&&value>=0&&value<=MAX_PROTOCOL_INTEGER}
7
8
  function requireFlags(flags,name){if(!Array.isArray(flags)||flags.some((flag)=>typeof flag!=="string"||!flag.length)){throw new Error("CanopyX: "+name+" must be an array of nonempty strings.")}}
8
9
  function checkDocument(doc,type){if(!doc||typeof doc!=="object"){throw new Error("CanopyX: expected a "+type+" document, got "+typeof doc)}
@@ -31,29 +32,21 @@ return tone}
31
32
  function applyTone(element,base,tone){element.className=base+tone.cls;toneColors(element,tone)}
32
33
  function toneColors(element,tone){if((element._tone||null)===tone.host){return}
33
34
  element._tone=tone.host;if(tone.host){element.style.setProperty("--cx-t","var(--cx-tone-"+tone.host+")");element.style.setProperty("--cx-t-bg","var(--cx-tone-"+tone.host+"-bg)")}else{element.style.removeProperty("--cx-t");element.style.removeProperty("--cx-t-bg")}}
34
- const numberFormats=new Map();function numberFormat(lang){const key=lang||"";let format=numberFormats.get(key);if(!format){try{format=new Intl.NumberFormat(lang||undefined,{maximumFractionDigits:3})}catch(e){format=new Intl.NumberFormat(undefined,{maximumFractionDigits:3})}
35
- if(numberFormats.size>=16)numberFormats.delete(numberFormats.keys().next().value);numberFormats.set(key,format)}
36
- return format}
37
35
  function pad2(value){return value<10?"0"+value:String(value)}
38
36
  const dateTexts=new Map();function dateText(value){let texts=dateTexts.get(value);if(texts===undefined){const time=Date.parse(value);const d=new Date(time);texts=isNaN(time)?null:[String(d.getUTCFullYear()).padStart(4,"0")+ "-"+pad2(d.getUTCMonth()+ 1)+ "-"+pad2(d.getUTCDate())+ " "+pad2(d.getUTCHours())+ ":"+pad2(d.getUTCMinutes()),String(d.getFullYear()).padStart(4,"0")+ "-"+pad2(d.getMonth()+ 1)+ "-"+pad2(d.getDate())+ " "+pad2(d.getHours())+ ":"+pad2(d.getMinutes())];if(dateTexts.size>=DATE_CACHE)dateTexts.clear();dateTexts.set(value,texts)}
39
37
  return texts}
40
- let DEFAULT_LABELS=Object.freeze({searchPlaceholder:"Search",searchResults:"Search results",searchResultCount:"{count} results",searchResultCountOfTotal:"{count} of {total} results",searchResultCountOfMany:"{count} of many results",searchNoResults:"No results",searchTooShort:"Type at least {min} characters",searchWordTooShort:"Type a word of at least {min} characters",searchFailed:"Search failed: {error}",columns:"Columns",visibleColumns:"Visible columns",resizeColumn:"Resize {column}",columnWidth:"{width} pixels",loading:"Loading…",loadError:"Could not load: {error} (Enter to retry)",queryFailed:"Could not update the view: {error}",showMore:"Show more ({loaded} of {total} loaded)",showMoreUnknownTotal:"Show more ({loaded} loaded)",pageLoaded:"{loaded} of {total} loaded",itemsLoaded:"{count} items",yes:"Yes",no:"No",utcTime:"{time} UTC",localTime:"{time} local time",mountError:"This view could not be displayed: {error}"});function labelsFor(...sources){const labels=Object.assign({},DEFAULT_LABELS);for(const source of sources){if(source==null)continue;if(typeof source!=="object"||Array.isArray(source))throw new Error("CanopyX: labels must be an object of strings.");Object.keys(source).forEach((name)=>{if(!Object.hasOwn(DEFAULT_LABELS,name))throw new Error("CanopyX: there is no label '"+name+"'.");if(typeof source[name]!=="string"||!/\S/.test(source[name])) throw new Error("CanopyX: label '" + name + "' must be a string with visible text.");
41
- if((name==="utcTime"||name==="localTime")&&source[name].indexOf("{time}")<0)throw new Error("CanopyX: label '"+name+"' must hold {time}.");labels[name]=source[name]})}
42
- return Object.freeze(labels)}
43
- function fill(template,values){return template.replace(/\{(\w+)\}/g, (match, name) => (values && Object.hasOwn(values, name) ? String(values[name]) : match));
44
- }
45
38
  function formatting(lang,local,labels){const split=(template)=>{const at=template.indexOf("{time}");return[template.slice(0,at),template.slice(at+6)]};return{lang,local,labels,utc:split(labels.utcTime),other:split(labels.localTime)}}
46
39
  function formatDate(value,fmt){const texts=dateText(value);return texts?(fmt.local?texts[1]:fmt.utc[0]+ texts[0]+ fmt.utc[1]):String(value)}
47
40
  function otherZone(cell,column,fmt){const value=cellValue(cell);if(column.kind!=="datetime"||typeof value!=="string"||(isCellObject(cell)&&typeof cell.text==="string")){return null}
48
41
  const texts=dateText(value);return texts?(fmt.local?fmt.utc[0]+ texts[0]+ fmt.utc[1]:fmt.other[0]+ texts[1]+ fmt.other[1]):null}
49
- function formatValue(value,kind,fmt){if(value===null||value===undefined){return ""}
42
+ function formatValue(value,kind,fmt,format){if(value===null||value===undefined){return ""}
50
43
  if(typeof value==="boolean"){return value?fmt.labels.yes:fmt.labels.no}
51
- if(typeof value==="number"&&kind==="number"){return numberFormat(fmt.lang).format(value)}
44
+ if(typeof value==="number"&&kind==="number"){return numberText(value,fmt.lang,format)}
52
45
  if(typeof value==="string"&&kind==="datetime"){return formatDate(value,fmt)}
53
46
  return String(value)}
54
47
  function cellText(cell,column,fmt){if(isCellObject(cell)&&typeof cell.text==="string"){return cell.text}
55
- return formatValue(cellValue(cell),column.kind,fmt)}
56
- const collator=new Intl.Collator(undefined,{numeric:true,sensitivity:"base"});function sortKey(cell,column){let value=cellValue(cell);if((value===null||value==="")&&isCellObject(cell)&&typeof cell.text==="string"&&cell.text!==""){value=cell.text}
48
+ return formatValue(cellValue(cell),column.kind,fmt,column.format)}
49
+ const collator=new Intl.Collator(undefined,{numeric:true,sensitivity:"base"});const safeGridNumber=value=>Number.isFinite(value)&&Math.abs(value)<=Number.MAX_SAFE_INTEGER;function sortKey(cell,column){let value=isCellObject(cell)&&(typeof cell.sort==="string"||safeGridNumber(cell.sort))?cell.sort:cellValue(cell);if((value===null||value==="")&&isCellObject(cell)&&typeof cell.text==="string"&&cell.text!==""){value=cell.text}
57
50
  if(value===null||value===""){return{rank:2,value:0}}
58
51
  if(typeof value==="boolean"){return{rank:0,value:value?1:0}}
59
52
  if(typeof value==="number"){return{rank:0,value}}
@@ -62,12 +55,46 @@ return{rank:1,value:String(value)}}
62
55
  function compareKeys(a,b,direction){if(a.rank!==b.rank){return a.rank-b.rank}
63
56
  if(a.rank===2){return 0}
64
57
  const order=a.rank===0?(a.value<b.value?-1:a.value>b.value?1:0):collator.compare(a.value,b.value);return order*direction}
58
+ let DEFAULT_LABELS=Object.freeze({searchPlaceholder:"Search",searchResults:"Search results",searchResultCount:"{count} results",searchResultCountOfTotal:"{count} of {total} results",searchResultCountOfMany:"{count} of many results",searchNoResults:"No results",searchTooShort:"Type at least {min} characters",searchWordTooShort:"Type a word of at least {min} characters",searchFailed:"Search failed: {error}",columns:"Columns",visibleColumns:"Visible columns",resizeColumn:"Resize {column}",columnWidth:"{width} pixels",loading:"Loading…",loadError:"Could not load: {error} (Enter to retry)",queryFailed:"Could not update the view: {error}",showMore:"Show more ({loaded} of {total} loaded)",showMoreUnknownTotal:"Show more ({loaded} loaded)",pageLoaded:"{loaded} of {total} loaded",itemsLoaded:"{count} items",yes:"Yes",no:"No",utcTime:"{time} UTC",localTime:"{time} local time",mountError:"This view could not be displayed: {error}"});function labelsFor(...sources){const labels=Object.assign({},DEFAULT_LABELS);for(const source of sources){if(source==null)continue;if(typeof source!=="object"||Array.isArray(source))throw new Error("CanopyX: labels must be an object of strings.");Object.keys(source).forEach((name)=>{if(!Object.hasOwn(DEFAULT_LABELS,name)&&!/^(?:gridEdit|format|calendar)[A-Z]\w{0,63}$/.test(name)) throw new Error("CanopyX: there is no label '" + name + "'.");
59
+ if(typeof source[name]!=="string"||!/\S/.test(source[name])) throw new Error("CanopyX: label '" + name + "' must be a string with visible text.");
60
+ if((name==="utcTime"||name==="localTime")&&source[name].indexOf("{time}")<0)throw new Error("CanopyX: label '"+name+"' must hold {time}.");labels[name]=source[name]})}
61
+ return Object.freeze(labels)}
62
+ function fill(template,values){return template.replace(/\{(\w+)\}/g, (match, name) => (values && Object.hasOwn(values, name) ? String(values[name]) : match));
63
+ }
64
+ const pluralRules=new Map();function plural(template,count,lang){if(!template.startsWith("["))return template;const forms={};for(const match of template.matchAll(/\[(zero|one|two|few|many|other)\]([^[]*)/g)) forms[match[1]] = match[2];
65
+ let rules=pluralRules.get(lang);if(!rules){try{rules=new Intl.PluralRules(lang||"en")}catch(_){rules=new Intl.PluralRules("en")}pluralRules.set(lang,rules)}
66
+ return forms[rules.select(Number(count))]??forms.other??template}
67
+ const numberFormats=new Map();function numberFormat(lang,minimum=0,maximum=3){const key=(lang||"")+ "|"+minimum+"|"+maximum;let format=numberFormats.get(key);if(!format){const digits={minimumFractionDigits:minimum,maximumFractionDigits:maximum};try{format=new Intl.NumberFormat(lang||undefined,digits)}catch(e){format=new Intl.NumberFormat(undefined,digits)}
68
+ if(numberFormats.size>=16)numberFormats.delete(numberFormats.keys().next().value);numberFormats.set(key,format)}
69
+ return format}
70
+ function numberText(value,lang,format){if(!format)return numberFormat(lang).format(value);const minimum=format.minimumFractionDigits??0,maximum=format.maximumFractionDigits??3;const magnitude=Math.abs(value),fixed=magnitude>=1e21?BigInt(magnitude).toString():magnitude.toFixed(maximum);const signed=value<0&&Number(fixed)!==0?"-"+fixed:fixed;const parts=numberFormat(lang,minimum,maximum).formatToParts(signed);const at=parts.findIndex(part=>!["minusSign","literal"].includes(part.type));return parts.map((part,index)=>(index===at&&format.prefix||"")+ part.value).join("")+(format.suffix||"")}
71
+ let pageNonce=typeof document!=="undefined"&&document.currentScript?.nonce||"",trustPolicy;function setNonce(nonce){if(typeof nonce!=="string"||!nonce)throw new Error("CanopyX: invalid nonce.");pageNonce=nonce}
72
+ function withNonce(element){const nonce=pageNonce||document.querySelector("script[nonce]")?.nonce;if(nonce)element.nonce=nonce;return element}
73
+ function trusted(kind,value){if(trustPolicy===undefined){try{trustPolicy=global.trustedTypes?.createPolicy("canopyx",{createHTML:text=>text,createScript:text=>text,createScriptURL:text=>text})||null}
74
+ catch(_){trustPolicy=null}}
75
+ return trustPolicy?trustPolicy["create"+kind](value):value}
76
+ function hostHtml(element,html,sanitize,kind){const text=String(html??""),value=sanitize?sanitize(text,{kind}):text;if(value?.nodeType)element.replaceChildren(value);else if(typeof value==="string")element.innerHTML=trusted("HTML",value);else if(global.trustedTypes?.isHTML?.(value))element.innerHTML=value;else throw new Error("CanopyX: invalid sanitizeHtml result.")}
77
+ DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{recordBack:"Back",recordBackTo:"Back to {record}",recordTrail:"Record trail",recordShowing:"Showing {record}",recordTabs:"Record views",recordSections:"Jump to a section",recordOtherFields:"Other fields",recordShowEmpty:"Show empty fields ({count})",recordHideEmpty:"Hide empty fields",recordActions:"Record actions",recordCopyValues:"Copy values",recordCopyJson:"Copy as JSON",recordCopyLink:"Copy link",recordCopied:"Copied to the clipboard",recordPrint:"Print record",recordExportCsv:"Export CSV",recordExportJson:"Export JSON",recordOpenAll:"Open all in grid",recordLoadMore:"Load more",recordCount:"{shown} of {total}",recordItems:"{count} items",recordMore:"+{count}",recordShowAll:"Show all {count}",recordRetry:"Retry",recordLoadFailed:"Could not load the record details: {error}",recordUnavailable:"Record {id} is not available here.",recordField:"Field",recordValue:"Value",recordSection:"Section",recordBefore:"Before",recordAfter:"After",recordAdded:"Added",recordRemoved:"Removed",recordChanged:"{count} changed fields"}));function boundedPattern(source,exact=true,whole=false){const bad=()=>{throw new Error("CanopyX: pattern requires literals, character classes and at most one variable repetition anchored at the start; groups, alternatives and backreferences are unsupported.")};if(typeof source!=="string"||source.length>256)bad();let width=0,variable=0;for(let i=0;i<source.length;){const unit=source[i++];if(unit==="^"&&i===1||unit==="$"&&i===source.length)continue;if(unit==="\\"){const escape=source[i++];if(!escape||!/^[dDsSwWbBnrtfv\\.^$*+?{}\[\]()|/]$/.test(escape)) bad();
78
+ }else if(unit==="["){let closed=false;while(i<source.length){const c=source[i++];if(c==="\\"){const escape=source[i++];if(!escape||!/^[dDsSwWbnrtfv\\.^$*+?{}\[\]()|/-]$/.test(escape)) bad(); }
79
+ else if(c==="]"){closed=true;break}}
80
+ if(!closed)bad()}else if("()|^$*+?{}]".includes(unit))bad();let count=1;if(i<source.length&&"*+?".includes(source[i])){variable++;i++}
81
+ else if(source[i]==="{"){const match=/^\{(\d+)(?:,(\d*))?\}/.exec(source.slice(i));
82
+ if(!match)bad();const low=Number(match[1]),high=match[2]===undefined?low:match[2]===""?Infinity:Number(match[2]);if(low>256||high<low||high!==Infinity&&high>256)bad();if(low!==high)variable++;count=low===high?low:1;i +=match[0].length}
83
+ width +=count;if(width>256||variable>1)bad()}
84
+ if(variable&&!whole&&source[0]!=="^")bad();return new RegExp(whole?"^(?:"+source+")$":source,exact?"u":"iu")}
85
+ function wildcardMatch(pattern,value,exact){if(!exact){pattern=pattern.toLowerCase();value=value.toLowerCase()}
86
+ let p=0,v=0,star=-1,retry=0;while(v<value.length){if(p<pattern.length&&(pattern[p]==="?"||pattern[p]===value[v])){p++;v++}
87
+ else if(pattern[p]==="*"){star=p++;retry=v}
88
+ else if(star>=0){p=star+1;v=++retry}
89
+ else return false}
90
+ while(pattern[p]==="*")p++;return p===pattern.length}
65
91
  const EXTRA_FOLD_PAIRS="1t.w.q.1,2u.w.n.1,2.w.7.1,y.1.o.2,4.1.3.2,3.1.8.2,3.1.n.2,2.-3d,1.1.3.2,4.5u,1.1,2.1,2.5q,1.1,2.5p,1.5p,1.1,3.27,1.5m,1.5n,1.1,2.5p,1.5r,2.5v,1.5t,1.1,4.5v,1.5x,2.5y,1.1.3.2,2.62,1.1,2.62,3.1,2.62,1.1,2.61,1.61,1.1,2.1,2.63,1.1,4.1,8.2,1.1,2.2,1.1,2.2,1.1.9.2,3.1.9.2,3.2,1.1,2.1,2.-2p,1.-1k,1.1.k.2,2.-3m,2.1.9.2,8.8bv,1.1,2.-4j,1.8bs,3.1,2.-5f,1.1x,1.1z,1.1.5.2,82.1,2.1,4.1,9.38,7.12,2.11.3.1,2.1s,2.1r,1.1r,2.w.h.1,2.w.9.1,10.8,9.1.c.2,6.-1o,3.1,2.-7,1.1,3.-3m.3.1,1.28.g.1,1.w.w.1,1d.1.h.2,a.1.r.2,2.f,1.1.7.2,3.1.1c.2,3.1c.12.1,28a.5ls.12.1,2.5ls,6.5ls,k3.tzk.28.1,1.8.6.1,1p0.1,7.-2bk.17.1,3.-2bk.3.1,8x.1.23.2,a.-5vj,2.1.1c.2,a.-8.8.1,9.-8.6.1,b.-8.8.1,9.-8.8.1,9.-8.6.1,c.-8.4.2,9.-8.8.1,p.-8.8.1,9.-8.8.1,9.-8.8.1,9.-8,1.-8,1.-22,1.-22,1.-9,c.-2e.4.1,1.-9,c.-8,1.-8,1.-2s,1.-2s,d.-8,1.-8,1.-34,1.-34,1.-7,c.-3k,1.-3k,1.-3i,1.-3i,1.-9,8a.-5st,4.-6gv,1.-6di,7.s,1a.g.g.1,k.1,mr.q.q.1,1f5.1c.1c.1,1d.1,2.-8af,1.-2xy,1.-89z,3.1.3.2,2.-8bg,1.-8al,1.-8bj,1.-8bi,2.1,3.1,9.-8cf,1.-8cf,1.1.1e.2,9.1,2.1,5.1,nym.1.n.2,k.1.e.2,3s.1.7.2,4.1.v.2,b.1,2.1,2.-r9g,1.1.5.2,5.1,2.-wmg,3.1,2.1,4.1.a.2,2.-wn8,1.-wnj,1.-wnf,1.-wn5,1.-wn8,2.-wlu,1.-wmi,1.-wlx,1.ps,1.1.8.2,2.-1c,1.-wn7,1.-raw,1.1,2.1,2.-wo7,1.1,4.1,6.1.3.2,2.-wu9,p.1,h7w.w.q.1";const EXTRA_LOWERCASE=new Map();let extraUnit=0;for(const record of EXTRA_FOLD_PAIRS.split(",")){const[gap,delta,count=1,step=0]=record.split(".").map(part=>parseInt(part,36));extraUnit +=gap;for(let i=0;i<count;i++)EXTRA_LOWERCASE.set(extraUnit+i*step,extraUnit+i*step+delta);extraUnit +=(count-1)*step}
66
92
  function foldCode(c){if(c<0x80){return c>=65&&c<=90?c+32:c}
67
93
  return EXTRA_LOWERCASE.get(c)||c}
68
94
  function foldUnit(unit){return String.fromCharCode(foldCode(unit.charCodeAt(0)))}
69
95
  function isSeparator(code){return code<128&&!((code>=48&&code<=57)||(code>=65&&code<=90)||(code>=97&&code<=122))}
70
- function foldText(text){const source=String(text==null?"":text);let out="";for(let i=0;i<source.length;i++){out +=foldUnit(source.charAt(i))}
96
+ function foldText(text){const source=String(text==null?"":text);if(!/[^\0-\x7f]/.test(source)) return source.toLowerCase();
97
+ let out="";for(let i=0;i<source.length;i++){out +=foldUnit(source.charAt(i))}
71
98
  return out}
72
99
  function words(text){const folded=foldText(text);const out=[];let current="";for(let i=0;i<folded.length;i++){if(isSeparator(folded.charCodeAt(i))){if(current){out.push(current);current=""}}else{current +=folded.charAt(i)}}
73
100
  if(current){out.push(current)}
@@ -77,6 +104,37 @@ function trimSeparators(text){let start=0,end=text.length;while(start<end&&isSep
77
104
  function rankLabel(label,foldedQuery){const l=trimSeparators(foldText(label));return l===foldedQuery?0:l.startsWith(foldedQuery)?1:2}
78
105
  function shardKey(prefix){let out="";for(let i=0;i<prefix.length;i++){const code=prefix.charCodeAt(i);out +=(code>=97&&code<=122)||(code>=48&&code<=57)?prefix.charAt(i):"_" +("000"+code.toString(16)).slice(-4)}
79
106
  return out}
107
+ function svgIcon(shapes){const ns="http://www.w3.org/2000/svg",svg=document.createElementNS(ns,"svg");svg.classList.add("cx-ico");svg.setAttribute("viewBox","0 0 24 24");svg.setAttribute("aria-hidden","true");for(const[,tag,attributes]of shapes.matchAll(/<(\w+)([^>]*)\/>/g)) {
108
+ const shape=svg.appendChild(document.createElementNS(ns,tag));for(const[,attribute,value]of attributes.matchAll(/([\w-]+)="([^"]*)"/g)) shape.setAttribute(attribute, value);
109
+ }
110
+ return svg}
111
+ function gridExportField(value,delimiter,typed=false){if(value==null)return "";let text=String(value);if(typeof value!=="number"&&!typed&&/^(?:[\t\r]|\s*[=+\-@\uFF1D\uFF0B\uFF0D\uFF20])/.test(text)) text = "'" + text;
112
+ return text.includes(delimiter)||/["\r\n]/.test(text) ? '"' + text.replace(/"/g, '""') + '"' : text;
113
+ }
114
+ function gridScalar(cell,column){const value=cellValue(cell);if(value==null)return null;if(column.kind==="number"){if(!safeGridNumber(value))
115
+ throw new Error("CanopyX: numeric grid cells need finite values within the safe integer magnitude.");return value}
116
+ if(column.kind==="datetime"){const date=typeof value==="string"&&/^\d{4}-\d\d-\d\dT.*(?:Z|[+-]\d\d:\d\d)$/.test(value) ? Date.parse(value) : NaN;
117
+ if(!Number.isFinite(date))
118
+ throw new Error("CanopyX: date grid cells need ISO dates with an explicit time zone.");return date}
119
+ if(typeof value!=="string"&&typeof value!=="boolean")throw new Error("CanopyX: text grid cells need strings or booleans.");return value}
120
+ function cellMatches(cell,predicate,column){const scalar=gridScalar(cell,column),op=predicate.operator;if(op==="isNull")return scalar===null;if(op==="isNotNull")return scalar!==null;if(scalar===null)return false;const normalize=value=>column.kind==="text"&&typeof value==="string"?foldText(value):value;const value=normalize(scalar),values=predicate.values.map(operand=>normalize(gridScalar(operand,column))),operand=values[0];switch(op){case "eq":return value===operand;case "ne":return value!==operand;case "in":return values.includes(value);case "contains":return String(value).includes(String(operand));case "startsWith":return String(value).startsWith(String(operand));case "gt":return value>operand;case "ge":return value>=operand;case "lt":return value<operand;case "le":return value<=operand}
121
+ return false}
122
+ function validateGridRows(rows,columns,ids=new Set()){if(!Array.isArray(rows))throw new Error("CanopyX: grid rows must be an array.");const declared=new Map(columns.map(column=>[column.id,column]));for(const row of rows){requireIdentifier(row&&row.id,"grid row ID");if(ids.has(row.id))throw new Error("CanopyX: duplicate grid row ID "+row.id);ids.add(row.id);if(!row.cells||typeof row.cells!=="object"||Array.isArray(row.cells))
123
+ throw new Error("CanopyX: invalid flat row fields.");gridDecoration(row.tone,row.className);for(const id of Object.keys(row.cells)){if(!declared.has(id))throw new Error("CanopyX: undeclared grid column "+id);const cell=row.cells[id];if(isCellObject(cell)){if(Array.isArray(cell)||
124
+ ["text","searchText","title","tone","secondary"].some(name=>cell[name]!==undefined&&typeof cell[name]!=="string")||
125
+ cell.secondary!==undefined&&cell.secondary.length>4096||cell.sort!==undefined&&!(typeof cell.sort==="string"?cell.sort.length<=4096:Number.isFinite(cell.sort))||cell.series!==undefined&&!gridSeriesValid(cell.series)||
126
+ cell.badges!==undefined&&(!Array.isArray(cell.badges)||cell.badges.some(badge=>!badge||typeof badge.text!=="string"||
127
+ ["tone","title"].some(name=>badge[name]!==undefined&&typeof badge[name]!=="string"))))
128
+ throw new Error("CanopyX: malformed grid cell.");if(typeof cell.sort==="number")gridScalar(cell.sort,{kind:"number"})}
129
+ gridScalar(cell,declared.get(id))}}
130
+ return rows}
131
+ function gridDecoration(tone,className){if(tone!==undefined&&(typeof tone!=="string"||!/^[a-z][a-z0-9-]{0,31}$/.test(tone) || tone.endsWith("-bg")) ||
132
+ className!==undefined&&(typeof className!=="string"||!className.trim()||className.length>1024||/[\u0000-\u001f\u007f-\u009f]/.test(className)))
133
+ throw new Error("CanopyX: invalid highlight tone or className.")}
134
+ function gridSeriesValid(points){return Array.isArray(points)&&points.length<=4096&&points.every(point=>point&&typeof point==="object"&&!Array.isArray(point)&&
135
+ typeof point.period==="string"&&!!point.period.trim()&&point.period.length<=256&&
136
+ (point.value==null||Number.isFinite(point.value)&&Math.abs(point.value)<=Number.MAX_SAFE_INTEGER)&&
137
+ (point.state==null||typeof point.state==="string"&&point.state.length<=4096)&&(point.tone===undefined||typeof point.tone==="string"))}
80
138
  const sidecarDocs=new Map();const sidecarWaiters=new Map();function sidecarKey(view,ref){return JSON.stringify([view,ref])}
81
139
  function acceptSidecar(entry){if(!Array.isArray(entry)||entry.length!==3){return}
82
140
  const key=sidecarKey(entry[0],entry[1]);const waiter=sidecarWaiters.get(key);if(waiter){sidecarWaiters.delete(key);waiter.resolve(entry[2])}else{sidecarDocs.set(key,entry[2])}}
@@ -84,7 +142,7 @@ const sidecarQueue=global.CanopyXSidecar=global.CanopyXSidecar||[];sidecarQueue.
84
142
  return 0};function loadSidecar(base,view,ref){if(ref.split("/").some((part)=>part==="."||part==="..")){return Promise.reject(new Error("Sidecar reference cannot contain a dot path component: "+ref))}
85
143
  const key=sidecarKey(view,ref);if(sidecarDocs.has(key)){const doc=sidecarDocs.get(key);sidecarDocs.delete(key);return Promise.resolve(doc)}
86
144
  const pending=sidecarWaiters.get(key);if(pending){return pending.promise}
87
- const waiter={};waiter.promise=new Promise((resolve,reject)=>{waiter.resolve=resolve;waiter.reject=reject});sidecarWaiters.set(key,waiter);const script=document.createElement("script");script.src=base+ref.split("/").map(encodeURIComponent).join("/")+ ".js";script.async=true;script.onload=()=>{script.remove();if(sidecarWaiters.get(key)===waiter){sidecarWaiters.delete(key);waiter.reject(new Error("Sidecar file did not register its data: "+ref))}};script.onerror=()=>{script.remove();if(sidecarWaiters.get(key)===waiter){sidecarWaiters.delete(key);waiter.reject(new Error("Sidecar file is missing: "+ref))}};document.head.appendChild(script);return waiter.promise}
145
+ const waiter={};waiter.promise=new Promise((resolve,reject)=>{waiter.resolve=resolve;waiter.reject=reject});sidecarWaiters.set(key,waiter);const script=withNonce(document.createElement("script"));script.src=trusted("ScriptURL",base+ref.split("/").map(encodeURIComponent).join("/")+ ".js");script.async=true;script.onload=()=>{script.remove();if(sidecarWaiters.get(key)===waiter){sidecarWaiters.delete(key);waiter.reject(new Error("Sidecar file did not register its data: "+ref))}};script.onerror=()=>{script.remove();if(sidecarWaiters.get(key)===waiter){sidecarWaiters.delete(key);waiter.reject(new Error("Sidecar file is missing: "+ref))}};document.head.appendChild(script);return waiter.promise}
88
146
  function badIndex(what){return new Error("CanopyX: search "+what+" does not match the manifest or is malformed.")}
89
147
  function isCount(value){return Number.isInteger(value)&&value>=0}
90
148
  function checkSearchIndex(index){if(!index||!isCount(index.entryCount)||!Array.isArray(index.entryFiles)||!Array.isArray(index.kinds)||
@@ -95,41 +153,11 @@ if(index.entryPacks===undefined&&index.shardPacks===undefined)return;const keys=
95
153
  (index.entryFiles.length>0)!==(index.entryPacks.length>0)||(keys.length>0)!==(index.shardPacks.length>0)||
96
154
  index.entryPacks.some((first,i)=>!isCount(first)||first>=index.entryFiles.length||(i?first<=index.entryPacks[i-1]:first))||
97
155
  index.shardPacks.some((key,i)=>typeof key!=="string"||!Object.hasOwn(index.shards,key)||(i?key<=index.shardPacks[i-1]:key!==keys[0]))){throw badIndex("index packs")}}
98
- function resolveShards(shards,keys,word){const bases=keys.filter((key)=>!/\.\d+$/.test(key));
99
- const parts=(selected)=>selected.flatMap((key)=>[key].concat(keys.filter((part)=>part.startsWith(key+"."))
100
- .sort((a,b)=>Number(a.slice(a.lastIndexOf(".")+ 1))- Number(b.slice(b.lastIndexOf(".")+ 1)))));for(let length=Math.min(2,word.length);length<=word.length;length++){const key=shardKey(word.slice(0,length));if(Object.hasOwn(shards,key))return parts([key]);const deeper=bases.filter((k)=>k.length>key.length&&k.startsWith(key));if(!deeper.length)return[];if(length===word.length)return parts(deeper)}
101
- return[]}
102
156
  function recentCache(limit,loadDoc,maxBytes=Infinity,bytesOf=()=>0){const cache=new Map();let bytes=0;const get=(key)=>{let entry=cache.get(key);if(entry){cache.delete(key)}else{entry={pending:loadDoc(key),ready:false,bytes:0};entry.pending.then(doc=>{if(cache.get(key)!==entry)return;entry.ready=true;entry.bytes=bytesOf(doc);bytes +=entry.bytes;for(const[oldKey,old]of cache){if(cache.size<=limit&&bytes<=maxBytes)break;if(old.ready){cache.delete(oldKey);bytes -=old.bytes}}},()=>{if(cache.get(key)===entry)cache.delete(key)})}
103
157
  cache.set(key,entry);return entry.pending};get.clear=()=>{cache.clear();bytes=0};return get}
104
- const packBytes=pages=>pages.reduce((bytes,text)=>bytes +(typeof text==="string"?text.length*2:0),0);function checkShard(doc,key,count,entryCount){checkDocument(doc,"searchShard");if(doc.key!==key)throw badIndex("shard key");if(!Array.isArray(doc.words)||!Array.isArray(doc.postings)||doc.words.length!==doc.postings.length)throw badIndex("shard");let total=0;doc.postings.forEach((list,i)=>{if(typeof doc.words[i]!=="string"||!Array.isArray(list)||!isCount(list[0]))throw badIndex("shard");let last=list[0];for(let j=1;j<list.length;j++){if(!Number.isInteger(list[j])||!list[j])throw badIndex("shard");last +=Math.abs(list[j]);total +=list[j]<0?-list[j]- 1:0}
105
- total +=list.length;if(last>=entryCount)throw badIndex("shard")});if(total!==count)throw badIndex("shard posting count");return doc}
106
- function mergePostings(lists){const out=[];let ascending=true;for(const list of lists){let value=0;list.forEach((step,i)=>{for(let n=i&&step<0?-step:1;n>0;n--){value=i?value +(step<0?1:step):step;if(out.length&&value<=out[out.length-1])ascending=false;out.push(value)}})}
107
- if(ascending)return out;out.sort((a,b)=>a-b);return out.filter((value,i)=>!i||value!==out[i-1])}
108
- function intersectPostings(a,b){let j=0;return a.filter((value)=>{while(j<b.length&&b[j]<value)j++;return b[j]===value})}
109
- function labelToken(folded){let hash=2166136261;for(let i=0;i<folded.length;i++){hash=Math.imul(hash^folded.charCodeAt(i),16777619)}
110
- return "\u0000" +("0000000" +(hash>>>0).toString(16)).slice(-8)}
111
- function entryReference(value){return value===undefined||value===null||isIdentifier(value)}
112
- function entryFile(doc,first,count){checkDocument(doc,"searchEntries");if(doc.first!==first||!Array.isArray(doc.stem)||!Array.isArray(doc.entries)||doc.entries.length!==count)throw badIndex("entry file");const stem=new Map(doc.stem.map((item)=>(Array.isArray(item)?[item[0],item.slice(1)]:[])));const records=new Map();const raw=(n)=>{const item=n>=first&&n<first+count?doc.entries[n-first]:stem.get(n);if(!Array.isArray(item)||item.length<5||item.length>8||!Number.isInteger(item[0])||item[0]<-1||item[0]>=n||
113
- !isCount(item[1])||typeof item[2]!=="string"||!isCount(item[3])||typeof item[4]!=="string"||
114
- (item[5]!=null&&!isCount(item[5]))||!entryReference(item[6])||!(item[7]===undefined||isIdentifier(item[7]))){throw badIndex("entry "+n)}
115
- return item};return(n)=>{const pending=[];for(let k=n;k>=0&&!records.has(k);k=raw(k)[0])pending.push(k);while(pending.length){const number=pending.pop();const item=raw(number);const parent=item[0]<0?null:records.get(item[0]);const base=parent?parent.id:"";if(item[1]+ item[3]>base.length)throw badIndex("entry "+number);records.set(number,{parent,id:base.slice(0,item[1])+ item[2]+ base.slice(base.length-item[3]),label:item[4],kind:item[5]==null?undefined:item[5],ref:item[6]||null,at:item[7]||null})}
116
- return records.get(n)}}
117
- function entryHit(record,kinds){const path=[];for(let r=record;r;r=r.parent)path.unshift(r.id);const hit={path,label:record.label};if(record.kind!==undefined){if(record.kind>=kinds.length)throw badIndex("entry kind");hit.kind=kinds[record.kind]}
118
- const parent=record.parent;if(parent)hit.context=parent.parent?parent.parent.label+" / "+parent.label:parent.label;validateSearchHit(hit);return hit}
119
- function entryFileOf(starts,n){let low=0;let high=starts.length-1;while(low<high){const mid=(low+high+1)>>1;if(starts[mid]<=n)low=mid;else high=mid-1}
120
- return low}
121
- function sidecarSearch(load,manifest,hints){const index=async()=>(await manifest()).search;const loadPack=recentCache(PACK_CACHE,async(number)=>{const doc=checkDocument(await load("q/"+number),"pack");if(!Array.isArray(doc.pages)||doc.pages.some(text=>typeof text!=="string"))throw badIndex("pack");return doc.pages},PACK_CACHE_BYTES,packBytes);const sortedKeys=new WeakMap();const packed=async(pack,slot)=>{const text=(await loadPack(pack))[slot];if(typeof text!=="string")throw badIndex("pack");return JSON.parse(text)};const loadIndexDoc=async(search,kind,name)=>{if(!search.entryPacks)return load(kind==="e"?"e/"+name:"s/k-"+name);if(kind==="e"){const pack=entryFileOf(search.entryPacks,name);return packed(pack,name-search.entryPacks[pack])}
122
- let keys=sortedKeys.get(search);if(!keys)sortedKeys.set(search,keys=Object.keys(search.shards).sort());let pack=0;while(pack+1<search.shardPacks.length&&search.shardPacks[pack+1]<=name)pack++;return packed(search.entryPacks.length+pack,keys.indexOf(name)- keys.indexOf(search.shardPacks[pack]))};const loadShard=recentCache(SHARD_CACHE,async(key)=>{const search=await index();return checkShard(await loadIndexDoc(search,"s",key),key,search.shards[key],search.entryCount)});const loadEntries=recentCache(ENTRY_CACHE,async(file)=>{const search=await index();const first=search.entryFiles[file];const end=file+1<search.entryFiles.length?search.entryFiles[file+1]:search.entryCount;return entryFile(await loadIndexDoc(search,"e",file),first,end-first)});async function postingsOf(search,keys,word){const lists=[];for(const shard of await Promise.all(resolveShards(search.shards,keys,word).map(loadShard))){shard.words.forEach((indexed,i)=>indexed.startsWith(word)&&lists.push(shard.postings[i]))}
123
- return mergePostings(lists)}
124
- async function run(query,queryWords,limit,sequence){const search=await index();if(!search){return{hits:[],total:0,truncated:false}}
125
- const keyWords=queryWords.filter((word,i)=>queryWords.indexOf(word)===i&&
126
- !queryWords.some((other)=>other.length>word.length&&other.startsWith(word)));const keys=Object.keys(search.shards);const folded=trimSeparators(foldText(query));const[exact,lists]=await Promise.all([postingsOf(search,keys,labelToken(folded)),Promise.all(keyWords.map((word)=>postingsOf(search,keys,word)))]);const candidates=lists.sort((a,b)=>a.length-b.length).reduce(intersectPostings);const order=exact.concat(candidates);const wanted=limit*SEARCH_CANDIDATES;const taken=new Set();const hits=[];const records=new Map();let next=0;while(next<order.length&&hits.length<limit){const files=[];let end=next;for(;end<order.length&&hits.length+end-next<wanted;end++){const file=entryFileOf(search.entryFiles,order[end]);if(files.indexOf(file)<0){if(files.length>=ENTRY_BATCH&&hits.length+end-next>=limit)break;files.push(file)}}
127
- const loaded=await Promise.all(files.map(loadEntries));for(;next<end;next++){const number=order[next];if(taken.has(number))continue;const record=loaded[files.indexOf(entryFileOf(search.entryFiles,number))](number);if(next>=exact.length||trimSeparators(foldText(record.label))===folded){taken.add(number);const hit=entryHit(record,search.kinds);records.set(hit,record);hits.push(hit)}}}
128
- const ranked=rankHits(hits,query,limit);ranked.total=candidates.length;ranked.truncated=candidates.length>ranked.hits.length;if(sequence===latest){hints.clear();ranked.hits.forEach((hit)=>{for(let r=records.get(hit);r;r=r.parent)hints.set(r.id,r)})}
129
- return ranked}
130
- let latest=0;return(query,limit)=>{const queryWords=words(query);if(!queryWords.length){return Promise.resolve({hits:[],total:0,truncated:false})}
158
+ const packBytes=pages=>pages.reduce((bytes,text)=>bytes +(typeof text==="string"?text.length*2:0),0);function sidecarSearch(load,manifest,hints){let search;return(query,limit)=>{const queryWords=words(query);if(!queryWords.length){return Promise.resolve({hits:[],total:0,truncated:false})}
131
159
  if(!queryWords.some((word)=>word.length>=2)){const error=new Error("CanopyX: sidecar search needs a word of at least two UTF-16 units.");error.code="too-short";return Promise.reject(error)}
132
- return run(query,queryWords,limit,++latest).catch((err)=>{loadShard.clear();loadEntries.clear();loadPack.clear();throw err})}}
160
+ return loadPart(SEARCH_PART).then(()=>(search||=SEARCH_IMPL.create(load,manifest,hints))(query,queryWords,limit))}}
133
161
  function slicePage(all,cursor,pageSize){const offset=cursor?Number(cursor)||0:0;const items=all.slice(offset,offset+pageSize);const end=offset+items.length;return{items,total:all.length,next:end<all.length?String(end):null,offset}}
134
162
  function rankHits(hits,query,limit){const folded=trimSeparators(foldText(query));const ranked=hits.map((hit)=>({hit,rank:rankLabel(hit.label,folded)}));ranked.sort((a,b)=>a.rank-b.rank||collator.compare(a.hit.label,b.hit.label));return{hits:ranked.slice(0,limit).map((item)=>item.hit),total:hits.length,truncated:hits.length>limit}}
135
163
  function inlineSource(view){const pageSize=view.pageSize||100;const labelWords=new WeakMap();const wordsOf=(node)=>{let cached=labelWords.get(node);if(!cached){cached=words(node.label);labelWords.set(node,cached)}
@@ -160,14 +188,15 @@ if(kind==="inline"){if(!Array.isArray(view.roots))throw new Error("CanopyX: inli
160
188
  throw new Error("CanopyX: unknown source kind '"+kind+"'.")}
161
189
  async function remoteJson(url,type,timeoutMs,lifetime,signal){const controller=new AbortController();const signals=Array.from(new Set([lifetime,signal].filter(Boolean)));const abort=()=>controller.abort();signals.forEach((item)=>{if(item.aborted)abort();else item.addEventListener("abort",abort,{once:true})});let timedOut=false;const timer=setTimeout(()=>{timedOut=true;abort()},timeoutMs);try{const response=await fetch(url,{headers:{Accept:"application/json"},signal:controller.signal});if(!response.ok){if(response.body)await response.body.cancel().catch(()=>{});throw new Error(response.status+" "+response.statusText)}
162
190
  return checkDocument(await response.json(),type)}catch(error){if(timedOut)throw new Error("CanopyX: remote request timed out after "+timeoutMs+" milliseconds.");throw error}finally{abort();clearTimeout(timer);signals.forEach((item)=>item.removeEventListener("abort",abort))}}
163
- class TreeGrid{constructor(host,options){const view=checkDocument(options.view,"view");requireIdentifier(view.id,"view ID");this.host=host;this.view=view;this.opts=options;for(const name of["isSelectable","beforeSelect"]){if(options[name]!==undefined&&typeof options[name]!=="function")throw new Error("CanopyX: "+name+" must be a function.")}
164
- this.id="cx" + ++instanceSeq;this.lifetime=new AbortController();this.source=options.source||sourceFor(view,{signal:this.lifetime.signal,timeoutMs:options.requestTimeoutMs});this.queryAware=this.source.queryAware===true;this.dataLifetime=new AbortController();this.acceptedQuery=Object.freeze({version:1,sort:null,filters:Object.freeze([])});this.datasetRevision=null;this.rowH=view.rowHeight||28;this.pageSize=view.pageSize||100;this.retainedNodeLimit=options.retainedNodeLimit===undefined?1024:options.retainedNodeLimit;if(!validStructuralCount(this.retainedNodeLimit))throw new Error("CanopyX: retainedNodeLimit must be a nonnegative 32-bit integer.");this.inactiveNodeLimit=options.inactiveNodeLimit===undefined?4096:options.inactiveNodeLimit;if(!validStructuralCount(this.inactiveNodeLimit))throw new Error("CanopyX: inactiveNodeLimit must be a nonnegative 32-bit integer.");this.multi=view.selection==="multiple";this.lang=document.documentElement.lang||undefined;this.labels=labelsFor(view.labels,options.labels);this.local=view.timeZone==="local";this.fmt=formatting(this.lang,this.local,this.labels);const renderers=hostRenderers(options.renderers||{},view.columns);if(!options.formatLink!==!options.parseLink){throw new Error("CanopyX: formatLink and parseLink define one link scheme and are given together.")}
191
+ class TreeGrid{constructor(host,options){const view=checkDocument(options.view,"view");requireIdentifier(view.id,"view ID");if(view.columns.some(column=>column.format!==undefined))throw new Error("CanopyX: number formats apply to flat grids only.");this.host=host;this.view=view;this.opts=options;for(const name of["isSelectable","beforeSelect","sanitizeHtml"]){if(options[name]!==undefined&&typeof options[name]!=="function")throw new Error("CanopyX: "+name+" must be a function.")}
192
+ this.id="cx" + ++instanceSeq;this.lifetime=new AbortController();this.source=options.source||sourceFor(view,{signal:this.lifetime.signal,timeoutMs:options.requestTimeoutMs});this.queryAware=this.source.queryAware===true;this.dataLifetime=new AbortController();this.acceptedQuery=Object.freeze({version:1,sort:null,filters:Object.freeze([])});this.datasetRevision=null;this.rowH=view.rowHeight||28;this.pageSize=view.pageSize||100;this.retainedNodeLimit=options.retainedNodeLimit===undefined?1024:options.retainedNodeLimit;if(!validStructuralCount(this.retainedNodeLimit))throw new Error("CanopyX: retainedNodeLimit must be a nonnegative 32-bit integer.");this.inactiveNodeLimit=options.inactiveNodeLimit===undefined?4096:options.inactiveNodeLimit;if(!validStructuralCount(this.inactiveNodeLimit))throw new Error("CanopyX: inactiveNodeLimit must be a nonnegative 32-bit integer.");this.multi=view.selection==="multiple";this.lang=document.documentElement.lang||undefined;this.labels=labelsFor(view.labels,options.labels);this.tools=Object.assign({},view.toolbar,options.toolbar);if(Object.entries(this.tools).some(([name,value])=>!/^(search|filters|columns|views|export|more|matches|layout)$/.test(name) || typeof value !== "boolean"))
193
+ throw new Error("CanopyX: toolbar options are booleans by control name.");this.local=view.timeZone==="local";this.fmt=formatting(this.lang,this.local,this.labels);this.kind="tree";this.plugins=viewPlugins(options,this.kind);const renderers=hostRenderers(options.renderers||{},view.columns,this.plugins);if(!options.formatLink!==!options.parseLink){throw new Error("CanopyX: formatLink and parseLink define one link scheme and are given together.")}
165
194
  this.columns=view.columns.map((column,index)=>({...column,hidden:index>0&&!!column.hidden,render:Object.hasOwn(renderers,column.id)?renderers[column.id]:null}));this.columns.forEach(prepareWidth);this.keyMap=new Map();this.root={key:null,h:null,parent:null,level:0,idx:0,expanded:true,children:null,total:null,next:null,loading:false,error:null};if(!options.source&&(!view.source||view.source.kind==="inline")){this.root.inlineItems=view.roots||[]}
166
- this.rows=[];this.tops=[0];this.total=0;this.version=1;this.rendered=new Map();this.pool=[];this.focused=null;this.selected=new Set();this.anchor=null;this.filter=null;this.declaredFilter=null;this.filterExpand=false;this.activeFilters=new Set();this.sortState=null;this.typeBuf="";this.typeAt=0;this.rafId=0;this.iconTemplates=this.buildIcons(view.icons||{});this.initializing=true;this.build();this.buildToolbar();this.ready=this.init()}
195
+ this.rows=[];this.tops=[0];this.total=0;this.version=1;this.rendered=new Map();this.pool=[];this.focused=null;this.selected=new Set();this.anchor=null;this.filter=null;this.declaredFilter=null;this.filterExpand=false;this.activeFilters=new Set();this.sortState=null;this.typeBuf="";this.typeAt=0;this.rafId=0;this.iconTemplates=this.buildIcons(view.icons||{});this.initializing=true;this.prepareViewModes();this.build();this.buildToolbar();if(view.deepLink)loadPart(LINKS).catch(()=>{});this.ready=this.modesReady?Promise.all([this.init(),this.modesReady]).then(([result])=>result):this.init()}
167
196
  async search(query,limit){if(this.destroyed)throw new DOMException("Grid was destroyed.","AbortError");const navigation=navigationScope(this,this.dataLifetime.signal);try{const result=await navigation.wait(this.source.search(query,limit,this.sourceRequest(this.dataLifetime.signal)));if(navigation.cancelled())throw new DOMException("Search snapshot was retired.","AbortError");if(this.queryAware)snapshotRevision(result,this.datasetRevision);return validateSearchResult(result)}finally{navigation.dispose()}}
168
- buildIcons(icons){const templates=new Map();Object.keys(icons).forEach((key)=>{const holder=h("span","cx-icon");holder.setAttribute("aria-hidden","true");holder.innerHTML=icons[key];// trusted host SVG, declared once per view
197
+ buildIcons(icons){const templates=new Map();Object.keys(icons).forEach((key)=>{const holder=h("span","cx-icon");holder.setAttribute("aria-hidden","true");hostHtml(holder,icons[key],this.opts.sanitizeHtml,"icon");// trusted host SVG, declared once per view
169
198
  templates.set(key,holder)});return templates}
170
- async init(){try{this.rootsReady=this.loadRoots();await this.rootsReady;await this.restoreState();await this.expandInitial(this.root);if(!this.linkChanged||!this.linkChanged.aborted)await this.openDeepLink()}finally{this.initializing=false}
199
+ async init(){try{this.rootsReady=this.loadRoots();await this.rootsReady;await this.toolsReady;await this.restoreState();await this.expandInitial(this.root);if(!this.linkChanged||!this.linkChanged.aborted)await this.openDeepLink()}finally{this.initializing=false}
171
200
  if(this.destroyed)return;await this.flushPendingToggles();if(this.linkChanged&&!this.linkChanged.aborted)await this.openDeepLink();if(!this.destroyed){this.host.setAttribute("data-cx-ready","true");this.emit("ready",{});this.scheduleRender()}}
172
201
  loadRoots(){if(!this.root.inlineItems){return this.loadPage(this.root,null)}
173
202
  validateNodes(this.root.inlineItems,this.keyMap,true);this.root.inline=true;const first=this.inlinePage(this.root,null);this.root.children=first.items.map((node,i)=>this.wrap(node,this.root,first.positions[i]));this.root.total=first.total;this.root.next=first.next;this.applySort(this.root,false);this.relayout();return Promise.resolve()}
@@ -229,7 +258,7 @@ throw new Error("CanopyX: query filters must be unique declared IDs.");const fil
229
258
  function snapshotRevision(value,expected){requireIdentifier(value&&value.revision,"dataset revision");if(expected!=null&&value.revision!==expected)throw new Error("CanopyX: dataset revision changed; reload the roots before continuing.");return value.revision}
230
259
  Object.assign(TreeGrid.prototype,{getQuery(){return{version:1,sort:this.sortState&&Object.assign({},this.sortState),filters:Array.from(this.activeFilters)}},sourceRequest(signal){return this.queryAware?{signal:signal||this.dataLifetime.signal,query:this.acceptedQuery,revision:this.datasetRevision}:{signal}},syncQueryControls(){this.toolbar.querySelectorAll("[data-cx-filter]").forEach(input=>{input.checked=this.activeFilters.has(input.getAttribute("data-cx-filter"))})},querySortHeader(id){const query=this.pendingQuery||this.acceptedQuery,state=query.sort&&query.sort.column===id?query.sort.direction:null;this.sort(id,state==="asc"?"desc":state==="desc"?null:"asc").catch(()=>{})}});Object.assign(TreeGrid.prototype,{retireWrapper(w){w._invalidated=true;this.retirePage(w);if(this.inlineRetained&&this.inlineRetained.get(w.key)===w)this.inlineRetained.delete(w.key);if(this.inactiveCache)this.inactiveCache.delete(w);if(this.keyMap.get(w.key)===w)this.keyMap.delete(w.key);w.children=null;w.inlineCache=null;w.inlineOrder=null;const selected=this.selected.delete(w);if(this.anchor===w)this.anchor=null;return selected?w:null}});Object.assign(TreeGrid.prototype,{build(){const host=this.host;host.classList.add("cx-host");if(!host.hasAttribute("data-cx-theme")){host.setAttribute("data-cx-theme","auto")}
231
260
  host.style.setProperty("--cx-row-h",this.rowH+"px");this.setTreeColumnPinned(this.view.pinTreeColumn);this.toolbar=h("div","cx-toolbar");this.toolbar.hidden=true;const grid=this.grid=h("div","cx-grid");grid.id=this.id;grid.setAttribute("role","treegrid");grid.setAttribute("aria-label",this.view.label);if(this.multi){grid.setAttribute("aria-multiselectable","true")}
232
- this.head=h("div","cx-head");this.head.setAttribute("role","rowgroup");this.headRow=h("div","cx-hrow");this.headRow.setAttribute("role","row");this.headRow.setAttribute("aria-rowindex","1");this.head.appendChild(this.headRow);this.canvas=h("div","cx-canvas");this.canvas.setAttribute("role","rowgroup");grid.appendChild(this.head);grid.appendChild(this.canvas);this.live=h("div","cx-live cx-sr-only");this.live.setAttribute("aria-live","polite");this.outline=h("div","cx-outline");host.appendChild(this.toolbar);host.appendChild(grid);host.appendChild(this.live);host.appendChild(this.outline);this.buildHeader();this.onScroll=()=>{this.scheduleRender();this.stateChanged()};this.onPrint=()=>{this.outline.innerHTML=this.toOutline()};grid.addEventListener("scroll",this.onScroll,{passive:true});grid.addEventListener("click",(e)=>this.onClick(e));grid.addEventListener("dblclick",(e)=>this.onDblClick(e));grid.addEventListener("keydown",(e)=>this.onKey(e));this.canvas.addEventListener("focusin",(e)=>{const rowEl=e.target.closest(".cx-row");if(rowEl&&rowEl._w&&this.focused!==rowEl._w){this.focusRevision=(this.focusRevision||0)+ 1;this.focused=rowEl._w;this.scheduleRender()}});this.canvas.addEventListener("focusout",(e)=>this.settleCell(e.relatedTarget));global.addEventListener("beforeprint",this.onPrint);this.onHash=(e)=>{const href=global.location.href;const last=this.locationEvent;this.locationEvent=last&&last.href===href&&last.type!==e.type?null:{href,type:e.type};if(this.locationEvent)this.readLocation()};global.addEventListener("hashchange",this.onHash);global.addEventListener("popstate",this.onHash);const viewWidth=()=>{if(grid.clientWidth)host.style.setProperty("--cx-view-w",grid.clientWidth+"px")};viewWidth();this.resizeObserver=new ResizeObserver(()=>{viewWidth();this.scheduleRender()});this.resizeObserver.observe(grid)},visibleColumns(){return this.columns.filter((column)=>!column.hidden)},buildHeader(){const active=document.activeElement;const focusedColumn=active&&this.headRow.contains(active)?active.closest(".cx-hcell").getAttribute("data-cx-column"):null;const focusedResize=active&&active.classList.contains("cx-resize");const nextCols=this.visibleColumns();const layoutChanged=!this.cols||this.cols.length!==nextCols.length||nextCols.some((column,i)=>column!==this.cols[i]);this.cols=nextCols;this.columnIndex=this.cols.map((column)=>this.columns.indexOf(column));if(layoutChanged){this.layout=(this.layout||0)+ 1;this.version++}
261
+ this.head=h("div","cx-head");this.head.setAttribute("role","rowgroup");this.headRow=h("div","cx-hrow");this.headRow.setAttribute("role","row");this.headRow.setAttribute("aria-rowindex","1");this.head.appendChild(this.headRow);this.canvas=h("div","cx-canvas");this.canvas.setAttribute("role","rowgroup");grid.appendChild(this.head);grid.appendChild(this.canvas);this.live=h("div","cx-live cx-sr-only");this.live.setAttribute("aria-live","polite");this.outline=h("div","cx-outline");host.appendChild(this.toolbar);host.appendChild(grid);host.appendChild(this.live);host.appendChild(this.outline);this.buildHeader();this.onScroll=()=>{this.scheduleRender();this.stateChanged()};this.onPrint=()=>{const table=this.outlineElement();this.outline.replaceChildren(...table?[table]:[])};grid.addEventListener("scroll",this.onScroll,{passive:true});grid.addEventListener("click",(e)=>this.onClick(e));grid.addEventListener("dblclick",(e)=>this.onDblClick(e));grid.addEventListener("pointerover",(e)=>this.cellTip(e));grid.addEventListener("keydown",(e)=>this.onKey(e));this.canvas.addEventListener("focusin",(e)=>{const rowEl=e.target.closest(".cx-row");if(rowEl&&rowEl._w&&this.focused!==rowEl._w){this.focusRevision=(this.focusRevision||0)+ 1;this.focused=rowEl._w;this.scheduleRender()}});this.canvas.addEventListener("focusout",(e)=>this.settleCell(e.relatedTarget));global.addEventListener("beforeprint",this.onPrint);this.onHash=(e)=>{const href=global.location.href;const last=this.locationEvent;this.locationEvent=last&&last.href===href&&last.type!==e.type?null:{href,type:e.type};if(this.locationEvent)this.readLocation()};global.addEventListener("hashchange",this.onHash);global.addEventListener("popstate",this.onHash);const viewWidth=()=>{if(grid.clientWidth)host.style.setProperty("--cx-view-w",grid.clientWidth+"px")};viewWidth();this.resizeObserver=new ResizeObserver(()=>{viewWidth();this.scheduleRender()});this.resizeObserver.observe(grid)},visibleColumns(){return this.columns.filter((column)=>!column.hidden)},buildHeader(){const active=document.activeElement;const focusedColumn=active&&this.headRow.contains(active)?active.closest(".cx-hcell").getAttribute("data-cx-column"):null;const focusedResize=active&&active.classList.contains("cx-resize");const nextCols=this.visibleColumns();const layoutChanged=!this.cols||this.cols.length!==nextCols.length||nextCols.some((column,i)=>column!==this.cols[i]);this.cols=nextCols;this.columnIndex=this.cols.map((column)=>this.columns.indexOf(column));if(layoutChanged){this.layout=(this.layout||0)+ 1;this.version++}
233
262
  this.grid.setAttribute("aria-colcount",String(this.cols.length));this.headRow.textContent="";this.headCells=this.cols.map((column,i)=>{const cell=h("div","cx-hcell cx-kind-" +(column.kind||"text"));cell.setAttribute("role","columnheader");cell.setAttribute("aria-label",column.title);cell.setAttribute("data-cx-column",column.id);cell.setAttribute("aria-colindex",String(i+1));if(column.sortable){const sorted=this.sortState&&this.sortState.column===column.id?this.sortState.direction:null;cell.setAttribute("aria-sort",sorted==="asc"?"ascending":sorted==="desc"?"descending":"none");const button=h("button","cx-sort");button.type="button";setText(button,column.title);button.addEventListener("click",(event)=>this.queryAware?this.querySortHeader(column.id,event):this.sort(column.id,sorted==="asc"?"desc":sorted==="desc"?null:"asc"));cell.appendChild(button)}else{const title=h("span","cx-htext");setText(title,column.title);cell.appendChild(title)}
234
263
  const grip=h("span","cx-resize");grip.setAttribute("aria-label",fill(this.labels.resizeColumn,{column:column.title}));grip.tabIndex=0;grip.setAttribute("role","separator");grip.setAttribute("aria-orientation","vertical");grip.setAttribute("aria-valuemin",column.minW);grip.setAttribute("aria-valuemax",column.maxW);grip.addEventListener("keydown",(e)=>{if(!["ArrowLeft","ArrowRight","Home"].includes(e.key))return;e.preventDefault();e.stopPropagation();column.width=columnWidth(column,e.key==="Home"?40:column.width +(e.key==="ArrowLeft"?-1:1)*(e.shiftKey?50:10));this.applyWidths();this.stateChanged()});grip.addEventListener("pointerdown",(e)=>this.startResize(e,column));cell.appendChild(grip);this.headRow.appendChild(cell);return cell});this.applyWidths();if(focusedColumn){const index=this.cols.findIndex((c)=>c.id===focusedColumn);const button=index>=0?this.headCells[index].querySelector(focusedResize?".cx-resize":".cx-sort"):null;if(button){button.focus()}}},rowCount(){return this.rows.length},rowItem(index){return this.rows[index]},rowTop(index){return this.tops[index]},rowAt(y){const tops=this.tops;let lo=0;let hi=this.rows.length-1;while(lo<hi){const mid=(lo+hi+1)>>1;if(tops[mid]<=y){lo=mid}else{hi=mid-1}}
235
264
  return Math.max(0,lo)},indexOf(w){const i=w?w._ri:undefined;return i!==undefined&&this.rows[i]===w?i:-1},render(){let measured=false;for(let pass=0;!this.destroyed;pass++){this.renderPass();if(!this.measureDetails())break;measured=true;if(pass===3){this.scheduleRender();break}}
@@ -263,7 +292,7 @@ return false}};Object.assign(TreeGrid.prototype,ROW_SCROLLING);const ROW_VIEWPOR
263
292
  const tabRow=visibleFocus||rows.rowItem(n?rows.rowAt(top):0)||null;const fragment=document.createDocumentFragment();for(const w of want){const index=rows.indexOf(w);if(index<0)continue;let rowEl=this.rendered.get(w);if(!rowEl){rowEl=free.pop()||this.pool.pop()||h("div");fragment.appendChild(rowEl);this.rendered.set(w,rowEl)}
264
293
  if(w._v!==this.version||rowEl._w!==w){this.fillRow(rowEl,w);w._v=this.version}else if(!w.status)this.refreshRowState(rowEl,w);const y=this.scaledScroll&&(index<first||index>last)?
265
294
  (index<first?-w.hpx-viewHeight:NATIVE_HEIGHT+viewHeight):rows.rowTop(index)- shift;if(rowEl._top!==y){rowEl.style.transform="translateY("+y+"px)";rowEl._top=y}
266
- if(rowEl._index!==index){rowEl.setAttribute("aria-rowindex",index+2);rowEl._index=index}
295
+ if(rowEl._index!==index){rowEl.setAttribute("aria-rowindex",index+2 +(this.headDepth||0));rowEl._index=index}
267
296
  const alt=index%2===1&&!w.status&&!w.h.detail&&(this.view.type!=="grid"||!!this.view.zebra);if(rowEl._alt!==alt){rowEl.classList.toggle("cx-alt",alt);rowEl._alt=alt}
268
297
  const tab=w===tabRow?0:-1;if(rowEl._tab!==tab){rowEl.tabIndex=tab;rowEl._tab=tab}}
269
298
  for(const rowEl of free){rowEl._w=null;this.observeDetail(rowEl,false);if(this.pool.length<200)this.pool.push(rowEl)}
@@ -274,13 +303,15 @@ Object.assign(TreeGrid.prototype,{fillRow(rowEl,w){rowEl._w=w;const kind=w.statu
274
303
  setAttr(rowEl,"aria-level",String(w.level));const auto=kind==="detail"&&w.auto;this.observeDetail(rowEl,auto);const height=auto?"auto":w.hpx+"px";if(rowEl._height!==height){rowEl.style.height=height;rowEl._height=height}
275
304
  if(kind==="status"){this.paintStatusRow(rowEl,w)}else if(kind==="detail"){this.paintDetailRow(rowEl,w)}else{this.paintNodeRow(rowEl,w)}},buildRow(rowEl,kind){disposeRowHostCells(rowEl);rowEl.textContent="";["aria-expanded","aria-busy","aria-setsize","aria-posinset","aria-selected","data-cx-id","data-cx-status","data-kind"].forEach((name)=>rowEl.removeAttribute(name));rowEl._attrs={};rowEl._cls=null;rowEl._kind=kind;rowEl._layout=this.layout;rowEl._height=null;rowEl.setAttribute("role","row");rowEl.style.width=this.rowWidth+"px";if(kind!=="node"){const cell=this.cell(rowEl,0,this.rowWidth);cell.setAttribute("aria-colspan",String(this.cols.length));rowEl._cells=[{cell}];if(kind==="status"){const indent=cell.appendChild(h("span","cx-indent"));const label=cell.appendChild(h("span"));rowEl._cells[0].indent=indent;rowEl._cells[0].label=label;rowEl._cells[0].text=label.appendChild(document.createTextNode(""))}
276
305
  return}
277
- rowEl._cells=this.cols.map((column,i)=>{const cell=this.cell(rowEl,i,column.width,column.kind);const slot={cell,base:cell.className +(i===0?" cx-tree":""),cls:null,title:null,badges:[],iconKey:null,iconEl:null,mode:"default"};cell.className=slot.base;if(i===0){slot.indent=cell.appendChild(h("span","cx-indent"));slot.exp=cell.appendChild(h("span","cx-exp"));slot.exp.setAttribute("aria-hidden","true");slot.content=cell.appendChild(h("span","cx-content"))}else{slot.content=cell}
306
+ rowEl._cells=this.cols.map((column,i)=>{const cell=this.cell(rowEl,i,column.renderWidth||column.width,column.kind);const slot={cell,base:cell.className +(i===0?" cx-tree":""),cls:null,title:null,badges:[],iconKey:null,iconEl:null,mode:"default"};cell.className=slot.base;if(i===0){slot.indent=cell.appendChild(h("span","cx-indent"));slot.exp=cell.appendChild(h("span","cx-exp"));slot.exp.setAttribute("aria-hidden","true");slot.content=cell.appendChild(h("span","cx-content"))}else{slot.content=cell}
278
307
  slot.textSpan=slot.content.appendChild(h("span","cx-text"));slot.text=slot.textSpan.appendChild(document.createTextNode(""));return slot})},paintStatusRow(rowEl,w){setClass(rowEl,"cx-row cx-status cx-"+w.status);setAttr(rowEl,"data-cx-status",w.status);const slot=rowEl._cells[0];slot.label.className=w.status==="more"?"cx-more-btn":"cx-status-text";slot.indent.style.width=(w.level-1)*INDENT+"px";if(slot.text.data!==w.h.label){slot.text.data=w.h.label}},paintDetailRow(rowEl,w){const node=w.h;setClass(rowEl,"cx-row cx-detail" +(w.auto?" cx-detail-auto":"")+(node.group?" cx-group":"")+(node.className?" "+node.className:""));this.paintRowState(rowEl,w);const old=rowEl._cells[0].cell;const cell=this.cell(rowEl,0,this.rowWidth);rowEl.replaceChild(cell,old);rowEl._cells[0].cell=cell;cell.setAttribute("aria-colspan",String(this.cols.length));const left=(w.level-1)*INDENT+8+"px";cell.style.paddingLeft=left;cell.style.setProperty("--cx-detail-left",left);this.renderDetail(node,cell);quietControls(cell);if(w.auto)this.measureNeeded=true},paintNodeRow(rowEl,w){const node=w.h;setClass(rowEl,"cx-row" +(node.group?" cx-group":"")+(node.className?" "+node.className:""));this.paintRowState(rowEl,w);setAttr(rowEl,"aria-expanded",this.expandable(w)?(w._open?"true":"false"):null);setAttr(rowEl,"aria-busy",w.loading?"true":null);const slots=rowEl._cells;for(let i=0;i<this.cols.length;i++){const slot=slots[i];if(i===0){this.paintTreeSlot(slot,w)}
279
308
  this.paintSlot(slot,node,this.cols[i],this.columnIndex[i],w)}},paintRowState(rowEl,w){const node=w.h;setAttr(rowEl,"data-cx-id",node.id);setAttr(rowEl,"data-kind",node.kind||null);setAttr(rowEl,"data-cx-status",null);setAttr(rowEl,"aria-selected",this.selected.has(w)?"true":"false");this.setPosition(rowEl,w)},refreshRowState(rowEl,w){setAttr(rowEl,"aria-expanded",this.expandable(w)?(w._open?"true":"false"):null);setAttr(rowEl,"aria-busy",w.loading?"true":null);this.setPosition(rowEl,w);if(rowEl._kind==="node")this.paintTreeSlot(rowEl._cells[0],w)},setPosition(rowEl,w){const parent=w.parent;const loaded=parent.children?parent.children.length:0;const size=parent.childrenFiltered?(parent.next?-1:parent.visibleCount):parent.total!=null?Math.max(parent.total,loaded):parent.next?-1:loaded;setAttr(rowEl,"aria-setsize",String(size));setAttr(rowEl,"aria-posinset",String(w.pos))},paintTreeSlot(slot,w){const level=w.level;if(slot.level!==level){slot.indent.style.width=(level-1)*INDENT+"px";slot.level=level}
280
309
  const expander=this.expandable(w)?"cx-exp":"cx-exp cx-leaf";if(slot.expCls!==expander){slot.exp.className=expander;slot.expCls=expander}
281
310
  const key=w.h.icon&&this.iconTemplates.has(w.h.icon)?w.h.icon:null;if(slot.iconKey!==key){if(slot.iconEl){slot.iconEl.remove()}
282
311
  slot.iconEl=key?this.iconTemplates.get(key).cloneNode(true):null;if(slot.iconEl){slot.cell.insertBefore(slot.iconEl,slot.content)}
283
- slot.iconKey=key}},paintSlot(slot,node,column,index,w){const value=this.cellOf(node,column,index);const hosted=column.render?this.paintHostSlot(slot,node,column,index,w):null;if(hosted==="host"){return}
312
+ slot.iconKey=key}},cellTip(event){const cell=event.target.closest?.(".cx-cell"),row=cell?.parentNode._kind==="node"&&cell.parentNode._w;const column=row&&!row.status&&this.cols[[...cell.parentNode.children].indexOf(cell)],tip=column?.tooltip;const source=tip==="auto"?column:this.columns.find(item=>item.id===tip);if(!source||cell.title&&!cell._cxTip)return;cell._cxTip=true;cell.title=tip!=="auto"||[cell,...cell.querySelectorAll("*")].some(item=>item.scrollWidth>item.clientWidth)?
313
+ cellText(this.cellOf(row.h,source,this.columns.indexOf(source)),source,this.fmt):""},paintSlot(slot,node,column,index,w){const value=this.cellOf(node,column,index);if(slot.cell._cxTip){slot.cell._cxTip=false;slot.cell.removeAttribute("title");slot.title=null}
314
+ const hosted=column.render?this.paintHostSlot(slot,node,column,index,w):null;if(hosted==="host"){return}
284
315
  const decorated=hosted==="decorated";const text=cellText(value,column,this.fmt);if(slot.text.data!==text){slot.text.data=text}
285
316
  const object=isCellObject(value);const tone=object?toneOf(value.tone):NO_TONE;const cls=slot.base+tone.cls;if(slot.cls!==cls){if(decorated){if(tone.cls)slot.cell.className +=tone.cls}else{slot.cell.className=cls}
286
317
  slot.cls=cls}
@@ -299,16 +330,17 @@ function hostBadge(badge){if(!badge||typeof badge!=="object"||typeof badge.text!
299
330
  return badgeElement(badge)}
300
331
  function attributeShape(element){let shape="";for(const attribute of element.attributes){shape +=attribute.name+"="+attribute.value+"\n"}
301
332
  return shape}
302
- function hostRenderers(renderers,columns){if(![Object.prototype,null].includes(Object.getPrototypeOf(renderers))||
333
+ function hostRenderers(renderers,columns,plugins){if(![Object.prototype,null].includes(Object.getPrototypeOf(renderers))||
303
334
  Object.keys(renderers).some(id=>typeof renderers[id]!=="function"||!columns.some(column=>column.id===id)))
304
- throw new Error("CanopyX: invalid column renderers.");return renderers}
335
+ throw new Error("CanopyX: invalid column renderers.");const named=Object.assign(Object.create(null),...plugins.map(item=>item.cellRenderers));return Object.assign(Object.create(null),...columns.filter(column=>typeof column.renderer==="string"&&named[column.renderer])
336
+ .map(column=>({[column.id]:named[column.renderer]})),renderers)}
305
337
  function disposeHostSlot(slot){const lifetime=slot.hostLifetime;if(!lifetime)return;delete slot.hostLifetime;lifetime.dispose()}
306
338
  function disposeRowHostCells(row){row._cells?.forEach(disposeHostSlot)}
307
339
  class HostCellContext{#controller;#cleanups;constructor(slot,column,level,expanded,index){slot.hostLifetime=this;this.column=column;this.level=level;this.expanded=expanded;this.index=index}
308
340
  get signal(){this.#controller||=new AbortController();if(this.#cleanups===null)this.#controller.abort();return this.#controller.signal}
309
341
  onDispose=cleanup=>{if(typeof cleanup!=="function")throw new TypeError("Invalid cleanup.");if(this.#cleanups===null)cleanup();else(this.#cleanups||=[]).push(cleanup)};get used(){return this.#controller||this.#cleanups}
310
342
  dispose(){const cleanups=this.#cleanups;this.#cleanups=null;this.#controller?.abort();while(cleanups?.length){try{cleanups.pop()()}catch(error){global.console?.error(error)}}}}
311
- Object.assign(TreeGrid.prototype,{paintHostSlot(slot,node,column,index,w){const tree=slot.content!==slot.cell;const previous=slot.content;const fresh=tree?h("span","cx-content"):this.newCell(this.cols.indexOf(column),column.width,column.kind);const shape=attributeShape(fresh);disposeHostSlot(slot);previous.replaceWith(fresh);this.setSlotContent(slot,fresh,tree);let drawn;try{drawn=column.render(node,fresh,column.hostRenderer===false?{column:column.id,level:w.level,expanded:w.expanded,index}:
343
+ Object.assign(TreeGrid.prototype,{paintHostSlot(slot,node,column,index,w){const tree=slot.content!==slot.cell;const previous=slot.content;const fresh=tree?h("span","cx-content"):this.newCell(this.cols.indexOf(column),column.renderWidth||column.width,column.kind);const shape=attributeShape(fresh);disposeHostSlot(slot);previous.replaceWith(fresh);this.setSlotContent(slot,fresh,tree);let drawn;try{drawn=column.render(node,fresh,column.hostRenderer===false?{column:column.id,level:w.level,expanded:w.expanded,index}:
312
344
  new HostCellContext(slot,column.id,w.level,w.expanded,index))===true}catch(e){disposeHostSlot(slot);if(fresh.parentNode)fresh.replaceWith(previous);this.setSlotContent(slot,previous,tree);throw e}
313
345
  const untouched=!drawn&&!slot.hostLifetime?.used&&!fresh.firstChild&&attributeShape(fresh)===shape;if(untouched&&slot.mode==="default"){disposeHostSlot(slot);fresh.replaceWith(previous);this.setSlotContent(slot,previous,tree);return "default"}
314
346
  if(tree){slot.cell.className=slot.base;slot.cell.removeAttribute("title");toneColors(slot.cell,NO_TONE)}
@@ -322,7 +354,7 @@ Object.assign(TreeGrid.prototype,{renderDetail(node,element,plainHtml=false){con
322
354
  const detail=node.detail;if(detail.type==="table"){const table=h("table","cx-detail-table");if(detail.caption){const caption=h("caption");setText(caption,detail.caption);table.appendChild(caption)}
323
355
  const columns=detail.columns||[];const widths=Array.isArray(detail.widths)&&detail.widths.length===columns.length?detail.widths:null;if(widths){const total=widths.reduce((sum,share)=>sum+share,0);const group=table.appendChild(h("colgroup"));widths.forEach((share)=>{group.appendChild(h("col")).style.width=(share / total * 100) + "%"; });
324
356
  }
325
- const head=h("thead");const headRow=h("tr");columns.forEach((title)=>{const th=h("th");th.scope="col";setText(th,title);headRow.appendChild(th)});head.appendChild(headRow);table.appendChild(head);const tbody=h("tbody");(detail.rows||[]).forEach((row)=>{const tr=h("tr");row.forEach((cell)=>tr.appendChild(detailCell("td",cell,this.fmt)));tbody.appendChild(tr)});table.appendChild(tbody);body.appendChild(table)}else if(detail.type==="properties"){const list=h("dl","cx-props");(detail.items||[]).forEach((item)=>{const term=h("dt");setText(term,item.name);list.appendChild(term);list.appendChild(detailCell("dd",item.value,this.fmt))});body.appendChild(list)}else if(detail.type==="html"){if(plainHtml)setText(body,detail.html||"");else body.innerHTML=detail.html;// trusted host markup, see docs/data-protocol.md
357
+ const head=h("thead");const headRow=h("tr");columns.forEach((title)=>{const th=h("th");th.scope="col";setText(th,title);headRow.appendChild(th)});head.appendChild(headRow);table.appendChild(head);const tbody=h("tbody");(detail.rows||[]).forEach((row)=>{const tr=h("tr");row.forEach((cell)=>tr.appendChild(detailCell("td",cell,this.fmt)));tbody.appendChild(tr)});table.appendChild(tbody);body.appendChild(table)}else if(detail.type==="properties"){const list=h("dl","cx-props");(detail.items||[]).forEach((item)=>{const term=h("dt");setText(term,item.name);list.appendChild(term);list.appendChild(detailCell("dd",item.value,this.fmt))});body.appendChild(list)}else if(detail.type==="html"){if(plainHtml)setText(body,detail.html||"");else hostHtml(body,detail.html,this.opts.sanitizeHtml,"detail");// trusted host markup, see docs/security.md
326
358
  }},detailHeight(detail){if(detail.height>0){return detail.height*this.rowH}
327
359
  const slots=detail.type==="table"?(detail.rows||[]).length +(detail.caption?2:1):detail.type==="properties"?(detail.items||[]).length:6;return Math.max(1,Math.min(DETAIL_ESTIMATE_SLOTS,slots))*this.rowH},observeDetail(rowEl,auto){if(!!rowEl._auto===auto){return}
328
360
  rowEl._auto=auto;if(!this.detailObserver){this.detailObserver=new ResizeObserver(()=>{this.measureNeeded=true;this.scheduleRender()})}
@@ -362,9 +394,10 @@ function cellControls(cell){const controls=Array.prototype.filter.call(cell.quer
362
394
  function quietControls(cell){cell.querySelectorAll(CELL_CONTROLS).forEach((control)=>{if(control.tabIndex>=0)control.tabIndex=-1})}
363
395
  Object.assign(TreeGrid.prototype,{rowCells(rowEl){return rowEl&&rowEl._cells&&rowEl._kind!=="status"?rowEl._cells.map((slot)=>slot.cell):[]},enterCells(w){const cells=this.rowCells(this.rendered.get(w));const index=cells.findIndex((cell)=>cellControls(cell).length>0);if(index<0)return false;this.focusCell(w,index,0);return true},focusCell(w,index,control,enter){this.setFocus(w,false);const cells=this.rowCells(this.rendered.get(w));if(!cells.length){this.setFocus(w,true);return}
364
396
  const cell=cells[Math.max(0,Math.min(cells.length-1,index))];const controls=cellControls(cell);let target=cell;if(typeof control==="number"&&controls.length)target=controls[control<0?controls.length-1:Math.min(control,controls.length-1)];else if(control===undefined&&controls.length===1)target=controls[0];let parked=-1;if(target!==cell&&!enter&&!target.classList.contains("cx-detail-body")&&target.matches(OWN_ARROWS)){parked=controls.indexOf(target);target=cell}
365
- cell._cxAt=parked;if(target===cell)cell.setAttribute("tabindex","-1");target.focus({preventScroll:true});this.revealInRow(cell,target,cells[0])},revealInRow(cell,target,treeCell){const grid=this.grid;if(cell.parentNode&&cell.parentNode._kind==="node"){this.revealInCell(cell,target);const pinned=cell!==treeCell&&this.host.classList.contains("cx-pin-tree")?treeCell.offsetWidth:0;const left=grid.getBoundingClientRect().left+grid.clientLeft;const box=cell.getBoundingClientRect();if(box.left<left+pinned)grid.scrollLeft -=left+pinned-box.left;else if(box.right>left+grid.clientWidth)grid.scrollLeft +=Math.min(box.right-left-grid.clientWidth,box.left-left-pinned)}
397
+ cell._cxAt=parked;if(target===cell)cell.setAttribute("tabindex","-1");target.focus({preventScroll:true});this.revealInRow(cell,target,cells[0])},revealInRow(cell,target,treeCell){const grid=this.grid;if(cell.parentNode&&cell.parentNode._kind==="node"){this.revealInCell(cell,target);const pins=cell.dataset.cxPin?[]:[...cell.parentNode.children].filter(item=>item!==cell&&
398
+ (item.dataset.cxPin||item===treeCell&&this.host.classList.contains("cx-pin-tree")));const edge=right=>pins.reduce((sum,item)=>sum +((item.dataset.cxPin==="right")===right?item.offsetWidth:0),0),pinned=edge(false),end=edge(true);const left=grid.getBoundingClientRect().left+grid.clientLeft;const box=cell.getBoundingClientRect();if(box.left<left+pinned)grid.scrollLeft -=left+pinned-box.left;else if(box.right>left+grid.clientWidth-end)grid.scrollLeft +=Math.min(box.right-left-grid.clientWidth+end,box.left-left-pinned)}
366
399
  const body=target.closest(".cx-detail-body");if(body&&body!==target){const outer=body.getBoundingClientRect();const inner=target.getBoundingClientRect();if(inner.top<outer.top)body.scrollTop -=outer.top-inner.top;else if(inner.bottom>outer.bottom)body.scrollTop +=Math.min(inner.bottom-outer.bottom,inner.top-outer.top)}
367
- const frame=grid.getBoundingClientRect();const top=frame.top+grid.clientTop+this.head.offsetHeight;const bottom=frame.top+grid.clientTop+grid.clientHeight;const box=target.getBoundingClientRect();if(box.top<top)this.setLogicalTop(this.logicalTop()-(top-box.top));else if(box.bottom>bottom)this.setLogicalTop(this.logicalTop()+ Math.min(box.bottom-bottom,box.top-top))},revealInCell(cell,target){if(target===cell||!cell.contains(target)||!cell.parentNode||cell.parentNode._kind!=="node")return;const outer=cell.getBoundingClientRect();const inner=target.getBoundingClientRect();const start=outer.left+cell.clientLeft;const end=start+cell.clientWidth;if(inner.left<start)cell.scrollLeft -=start-inner.left;else if(inner.right>end)cell.scrollLeft +=Math.min(inner.right-end,inner.left-start);if(cell.scrollLeft)this.scrolledCell=cell},settleCell(next){const cell=this.scrolledCell;if(cell&&!cell.contains(next||document.activeElement)){cell.scrollLeft=0;this.scrolledCell=null}},focusBeforeRows(event){const previous=this.headRow.querySelector(".cx-hcell:last-child .cx-resize");if(!previous)return;event.preventDefault();previous.focus()},heldCell(){const active=document.activeElement;const rowEl=active&&active.closest?active.closest(".cx-row"):null;if(!rowEl||rowEl===active||!rowEl._w||!this.canvas.contains(rowEl))return null;const cells=this.rowCells(rowEl);const index=cells.findIndex((cell)=>cell.contains(active));if(index<0)return null;return{w:rowEl._w,index,control:active===cells[index]?-1:Array.prototype.indexOf.call(cells[index].querySelectorAll(CELL_CONTROLS),active)}},restoreCell(held){const active=document.activeElement;if(!held||this.destroyed||(active&&active!==document.body)||!this.rendered.has(held.w))return;const cell=this.rowCells(this.rendered.get(held.w))[held.index];if(!cell)return;const candidate=held.control>=0?cell.querySelectorAll(CELL_CONTROLS)[held.control]:null;const target=candidate&&reachable(candidate)?candidate:cell;if(target===cell)cell.setAttribute("tabindex","-1");target.focus({preventScroll:true});this.revealInCell(cell,target)},onCellKey(e){const rowEl=e.target.closest(".cx-row");const w=rowEl&&rowEl._w;const cells=this.rowCells(rowEl);const index=cells.findIndex((cell)=>cell.contains(e.target));if(!w||w.status||index<0||e.isComposing||e.defaultPrevented)return index>=0;const cell=cells[index];const control=e.target!==cell?e.target:null;if(control&&control.matches(OWN_ARROWS)){if(e.key!=="Escape")return true;const from=cellControls(cell).indexOf(control);this.focusCell(w,index,null);cell._cxAt=from;e.preventDefault();e.stopPropagation();return true}
400
+ const frame=grid.getBoundingClientRect();const top=frame.top+grid.clientTop+this.head.offsetHeight;const bottom=frame.top+grid.clientTop+grid.clientHeight;const box=target.getBoundingClientRect();if(box.top<top)this.setLogicalTop(this.logicalTop()-(top-box.top));else if(box.bottom>bottom)this.setLogicalTop(this.logicalTop()+ Math.min(box.bottom-bottom,box.top-top))},revealInCell(cell,target){if(target===cell||!cell.contains(target)||!cell.parentNode||cell.parentNode._kind!=="node")return;const outer=cell.getBoundingClientRect();const inner=target.getBoundingClientRect();const start=outer.left+cell.clientLeft;const end=start+cell.clientWidth;if(inner.left<start)cell.scrollLeft -=start-inner.left;else if(inner.right>end)cell.scrollLeft +=Math.min(inner.right-end,inner.left-start);if(cell.scrollLeft)this.scrolledCell=cell},settleCell(next){const cell=this.scrolledCell;if(cell&&!cell.contains(next||document.activeElement)){cell.scrollLeft=0;this.scrolledCell=null}},focusBeforeRows(event){const previous=this.headRow.querySelector(".cx-hcell:last-child .cx-resize");if(!previous)return;event.preventDefault();previous.focus()},heldCell(){const active=document.activeElement;const rowEl=active&&active.closest?active.closest(".cx-row"):null;if(!rowEl||rowEl===active||!rowEl._w||!this.canvas.contains(rowEl))return null;const cells=this.rowCells(rowEl);const index=cells.findIndex((cell)=>cell.contains(active));if(index<0)return null;return{w:rowEl._w,index,control:active===cells[index]?-1:Array.prototype.indexOf.call(cells[index].querySelectorAll(CELL_CONTROLS),active)}},restoreCell(held){const active=document.activeElement;if(!held||this.destroyed||(active&&active!==document.body)||!this.rendered.has(held.w))return;const cell=this.rowCells(this.rendered.get(held.w))[held.index];if(!cell)return;const candidate=held.control>=0?cell.querySelectorAll(CELL_CONTROLS)[held.control]:null;const target=candidate&&reachable(candidate)?candidate:cell;if(target===cell)cell.setAttribute("tabindex","-1");target.focus({preventScroll:true});this.revealInCell(cell,target)},onCellKey(e){const rowEl=e.target.closest(".cx-row");const w=rowEl&&rowEl._w;const cells=this.rowCells(rowEl);const index=cells.findIndex((cell)=>cell.contains(e.target));if(!w||w.status&&!w.group||index<0||e.isComposing||e.defaultPrevented)return index>=0;const cell=cells[index];const control=e.target!==cell?e.target:null;if(control&&control.matches(OWN_ARROWS)){if(e.key!=="Escape")return true;const from=cellControls(cell).indexOf(control);this.focusCell(w,index,null);cell._cxAt=from;e.preventDefault();e.stopPropagation();return true}
368
401
  const controls=cellControls(cell);const at=control?controls.indexOf(control):cell._cxAt>=0&&cell._cxAt<controls.length?cell._cxAt:-1;const detail=rowEl._kind==="detail";switch(e.key){case "ArrowRight":
369
402
  if(at>=0&&at<controls.length-1)this.focusCell(w,index,at+1);else if(!detail&&index<cells.length-1)this.focusCell(w,index+1,0);break;case "ArrowLeft":
370
403
  if(at>0)this.focusCell(w,index,at-1);else if(!detail&&index>0)this.focusCell(w,index-1,-1);else this.setFocus(w,true);break;case "Home":
@@ -406,8 +439,7 @@ if(w.status){this.setFocus(w,true);this.activate(w,true);return}
406
439
  const interactive=hostControl(e.target);if(interactive&&!e.target.closest(".cx-exp")){this.setFocus(w,false);return}
407
440
  if(w.h.detail){this.setFocus(w,true);this.select(w,{toggle:e.ctrlKey||e.metaKey,range:e.shiftKey});return}
408
441
  this.setFocus(w,true);if(e.target.classList.contains("cx-exp")){this.toggle(w)}else{this.select(w,{toggle:e.ctrlKey||e.metaKey,range:e.shiftKey})}},onDblClick(e){const w=this.rowFromEvent(e);if(w&&!w.status&&!hostControl(e.target)){this.toggle(w)}},setFilter(filter,options){this.filter=typeof filter==="function"?{match:filter}:filter||null;this.cacheDirty=true;this.filterExpand=!!(options&&options.autoExpand);this.keyMap.forEach((w)=>{w.filterCollapsed=false});this.relayout()},setFilterActive(id,active){return this.updateActiveFilters(active?{add:[id]}:{remove:[id]})},updateActiveFilters(change){const{add=[],remove=[]}=change||{};if(!change||typeof change!=="object"||!Array.isArray(add)||!Array.isArray(remove))throw new Error("CanopyX: filter changes need add/remove arrays.");this.validateFilterIds(add.concat(remove));const next=new Set(this.queryAware?(this.pendingQuery||this.acceptedQuery).filters:this.activeFilters);remove.forEach(id=>next.delete(id));add.forEach(id=>next.add(id));return this.setActiveFilters(Array.from(next))},setActiveFilters(ids){if(!Array.isArray(ids))throw new Error("CanopyX: setActiveFilters needs an array of filter ids.");if(this.queryAware)return this.setQuery({version:1,sort:(this.pendingQuery||this.acceptedQuery).sort,filters:ids});this.applyActiveFilters(ids,new Set(ids))},validateFilterIds(ids){const declared=this.view.filters||[];ids.forEach((id)=>{if(!declared.some((f)=>f.id===id))throw new Error("CanopyX: the view declares no filter '"+id+"'.")})},applyActiveFilters(named,next){this.validateFilterIds(named);const declared=this.view.filters||[];const changed=next.size!==this.activeFilters.size||Array.from(next).some((id)=>!this.activeFilters.has(id));if(!changed&&this.filter===this.declaredFilter)return;this.activeFilters=next;this.toolbar.querySelectorAll("[data-cx-filter]").forEach((input)=>{input.checked=next.has(input.getAttribute("data-cx-filter"))});const filters=declared.filter((f)=>next.has(f.id));const has=(node,flag)=>!!(flag&&node.flags&&node.flags.indexOf(flag)>=0);this.setFilter(filters.length?{match:(node)=>filters.every((f)=>has(node,f.flag)),keep:(node)=>filters.every((f)=>has(node,f.flag)||has(node,f.ancestorFlag))}:null,{autoExpand:true});this.declaredFilter=this.filter;if(changed)this.emit("filter",{active:Array.from(next)});this.stateChanged()},sort(columnId,direction){if(this.queryAware)return this.setQuery({version:1,sort:direction?{column:columnId,direction}:null,filters:(this.pendingQuery||this.acceptedQuery).filters});this.sortState=direction?{column:columnId,direction}:null;this.inlineOrderVersion=(this.inlineOrderVersion||0)+ 1;this.buildHeader();this.applySort(this.root,true);this.relayout();this.stateChanged()},setColumnVisible(columnId,visible){const index=this.columns.findIndex((c)=>c.id===columnId);if(index<=0){return}
409
- this.columns[index].hidden=!visible;const input=Array.from(this.toolbar.querySelectorAll("[data-cx-column]")).find((el)=>el.getAttribute("data-cx-column")===columnId);if(input){input.checked=!!visible}
410
- this.buildHeader();this.relayout();this.stateChanged()}});function navigationScope(grid,signal,dataBound=false){const signals=Array.from(new Set([grid.lifetime.signal,dataBound&&grid.dataLifetime.signal].concat(signal||[]).filter(Boolean)));const controller=new AbortController();let abort;const stopped=new Promise((resolve)=>{abort=()=>{controller.abort();resolve()}});signals.forEach((item)=>item.addEventListener("abort",abort,{once:true}));const cancelled=()=>signals.some((item)=>item.aborted);if(cancelled())abort();return{cancelled,signal:controller.signal,wait:(pending)=>Promise.race([pending,stopped]),dispose:()=>{signals.forEach((item)=>item.removeEventListener("abort",abort));controller.abort()}}}
442
+ this.columns[index].hidden=!visible;this.syncColumnControls();this.buildHeader();this.relayout();this.stateChanged()}});function navigationScope(grid,signal,dataBound=false){const signals=Array.from(new Set([grid.lifetime.signal,dataBound&&grid.dataLifetime.signal].concat(signal||[]).filter(Boolean)));const controller=new AbortController();let abort;const stopped=new Promise((resolve)=>{abort=()=>{controller.abort();resolve()}});signals.forEach((item)=>item.addEventListener("abort",abort,{once:true}));const cancelled=()=>signals.some((item)=>item.aborted);if(cancelled())abort();return{cancelled,signal:controller.signal,wait:(pending)=>Promise.race([pending,stopped]),dispose:()=>{signals.forEach((item)=>item.removeEventListener("abort",abort));controller.abort()}}}
411
443
  Object.assign(TreeGrid.prototype,{async openForPath(w,navigation){if(navigation.cancelled()||w._invalidated)return false;const wasOpen=w.expanded&&!w.filterCollapsed;w.expanded=true;w.filterCollapsed=false;if(!wasOpen)this.relayout();const revision=w.toggleRevision||0;if(!w.children){await navigation.wait(this.loadPage(w,null));if(navigation.cancelled()||w._invalidated||!w.expanded||(w.toggleRevision||0)!==revision)return false;if(!w.children){throw new Error("CanopyX: could not load the children of '"+w.key+"': " +(w.error||"no data"))}}else if(!wasOpen){await navigation.wait(this.expandInitial(w))}
412
444
  return!navigation.cancelled()&&!w._invalidated&&w.expanded&&(w.toggleRevision||0)===revision},async expandPath(ids,options){const navigation=navigationScope(this,options&&options.signal,true);try{return await this.navigatePath(ids,options,navigation)}
413
445
  finally{navigation.dispose()}},async navigatePath(ids,options,navigation){const o=options||{};const cancelled=navigation.cancelled;if(cancelled())return null;await navigation.wait(this.rootsReady);if(cancelled())return null;await navigation.wait(this.prefetchPath(ids,!!o.expandLast,navigation.signal));if(cancelled())return null;const release=this.holdLayout();let w;let rebuilt=false;try{w=await this.openPath(ids,o,navigation)}finally{rebuilt=release()}
@@ -451,9 +483,15 @@ await navigation.wait(this.source.page(parent===this.root?null:parent.h,cursor,t
451
483
  if(page.positions)positions.push(...page.positions);items.push(...page.items);total=page.total;next=page.next||null;if(next&&(seen.has(next)||next===cursor))throw new Error("CanopyX: reload continuation did not advance.");if(next)seen.add(next);cursor=next}while(next&&items.length<target);if(navigation.cancelled()||parent._invalidated)return false;const current=new Map(this.keyMap);for(const key of this.descendants(parent).keys())current.delete(key);validateNodes(isInline?inline:items,current,inlineOnly);const anchor=this.captureScrollAnchor();this.retirePage(parent,controller);if(parent===this.root&&inlineOnly)parent.inlineItems=inline;this.reconcileList(parent,items,total==null?next?null:items.length:total,next,isInline?positions:null);this.restoreScrollAnchor(anchor);this.stateChanged();this.emit("reload",{node:parent.h,count:items.length,total:parent.total,ms:performance.now()- started});return true}catch(error){if(navigation.cancelled()||parent._invalidated)return false;this.emit("error",{node:parent.h,message:error.message||String(error)});throw error}finally{navigation.dispose();controller.abort();if(parent.reloadController===controller)parent.reloadController=null}},captureScrollAnchor(){const top=this.logicalTop();const index=this.rowAt(top);const w=this.rows[Math.min(index,this.rows.length-1)];return w&&!w.status?{id:w.key,offset:top-this.tops[this.indexOf(w)]}:null},restoreScrollAnchor(anchor){const w=anchor&&this.keyMap.get(anchor.id),index=this.indexOf(w);if(index>=0)this.setLogicalTop(Math.max(0,this.tops[index]+ anchor.offset))}});Object.assign(TreeGrid.prototype,{async setQuery(value,options,restoreOwner){if(this.destroyed)return false;if(options&&options.signal&&options.signal.aborted)return false;if(!this.queryAware)throw new Error("CanopyX: this source does not support view queries.");const query=readQuery(this,value),force=!!(options&&options.force);if(this.restoreController&&this.restoreController!==restoreOwner)this.restoreController.abort();if(this.queryController)this.queryController.abort();if(!force&&this.datasetRevision!=null&&JSON.stringify(query)===JSON.stringify(this.acceptedQuery)){this.pendingQuery=null;this.syncQueryControls();return true}
452
484
  const controller=this.queryController=new AbortController();const navigation=navigationScope(this,[controller.signal,options&&options.signal]);this.pendingQuery=query;const started=performance.now(),target=force?Math.max(this.pageSize,(this.root.children||[]).length):1;try{const items=[],seen=new Set(),existing=new Map();let cursor=null,next=null,total=null,revision=null;do{if(navigation.cancelled())return false;const page=await navigation.wait(this.source.page(null,cursor,{signal:controller.signal,query,revision}));if(navigation.cancelled())return false;validatePage(page,existing,items.length);revision=snapshotRevision(page,revision);for(const node of page.items){const pending=[node];while(pending.length){const data=pending.pop();existing.set(data.id,true);for(const child of data.children||[])pending.push(child)}}
453
485
  items.push(...page.items);total=page.total;next=page.next||null;if(next&&(next===cursor||seen.has(next)))throw new Error("CanopyX: query continuation did not advance.");if(next)seen.add(next);cursor=next}while(next&&items.length<target);if(navigation.cancelled())return false;const anchor=force?this.captureScrollAnchor():null;const filtersChanged=JSON.stringify(query.filters)!==JSON.stringify(this.acceptedQuery.filters);this.applyQuerySnapshot(query,revision,items,total,next,anchor);if(navigation.cancelled())return false;this.emit("query",{query:this.getQuery(),revision});if(filtersChanged&&!navigation.cancelled())this.emit("filter",{active:Array.from(this.activeFilters)});if(force&&!navigation.cancelled())this.emit("reload",{node:null,count:items.length,total:this.root.total,ms:performance.now()- started});if(!this.restoring&&!navigation.cancelled())await navigation.wait(this.expandInitial(this.root));return!navigation.cancelled()}catch(error){if(navigation.cancelled())return false;this.announce(fill(this.labels.queryFailed,{error:error.message||String(error)}));this.emit("error",{node:null,message:error.message||String(error)});throw error}finally{navigation.dispose();controller.abort();if(this.queryController===controller){this.queryController=null;this.pendingQuery=null;this.syncQueryControls()}}},applyQuerySnapshot(query,revision,items,total,next,anchor){this.dataLifetime.abort();this.dataLifetime=new AbortController();if(this.cancelSearch)this.cancelSearch();this.retirePage(this.root);for(const node of this.keyMap.values())this.retirePage(node);if(this.inactiveCache)this.inactiveCache.clear();this.acceptedQuery=query;this.datasetRevision=revision;this.sortState=query.sort;this.activeFilters=new Set(query.filters);this.filter=null;this.declaredFilter=null;this.buildHeader();this.syncQueryControls();this.reconcileList(this.root,items,total==null?next?null:items.length:total,next,null,false,!!this.restoring,true);this.rootsReady=Promise.resolve();if(anchor)this.restoreScrollAnchor(anchor);else this.setLogicalTop(0);const count=numberFormat(this.lang);this.announce(this.root.total!=null&&items.length<this.root.total?fill(this.labels.pageLoaded,{loaded:count.format(items.length),total:count.format(this.root.total)}):
454
- fill(this.labels.itemsLoaded,{count:count.format(items.length)}));this.stateChanged()}});Object.assign(TreeGrid.prototype,{buildToolbar(){const bar=this.toolbar;let any=false;if(this.view.search&&this.source.canSearch){this.buildSearch(bar,this.view.search);any=true}
455
- (this.view.filters||[]).forEach((filter)=>{if(filter.hidden)return;const label=h("label","cx-switch");const input=h("input");input.type="checkbox";input.setAttribute("data-cx-filter",filter.id);input.addEventListener("change",()=>{const pending=this.setFilterActive(filter.id,input.checked);if(pending&&pending.catch)pending.catch(()=>{})});label.appendChild(input);label.appendChild(document.createTextNode(filter.label));bar.appendChild(label);any=true});if(this.columns.length>1){this.buildColumnChooser(bar);any=true}
456
- bar.hidden=!any},clearDeclaredFilters(){if(!this.activeFilters.size){return}
486
+ fill(this.labels.itemsLoaded,{count:count.format(items.length)}));this.stateChanged()}});const EXPORT_FORMATS=new Map(),EXPORT_IMPL={},exportFormatId=id=>typeof id==="string"&&/^[a-z][a-z\d-]{0,31}$/.test(id);
487
+ const exportFormatIds=list=>Array.isArray(list)&&new Set(list).size===list.length&&list.every(exportFormatId);function exportFormat(format){if(!format||!exportFormatId(format.id)||format.id==="csv"||
488
+ typeof format.label!=="string"||!format.label.trim()||typeof format.extension!=="string"||!/^[a-z\d]{1,16}$/i.test(format.extension) ||
489
+ format.mime!==undefined&&typeof format.mime!=="string"||typeof format.write!=="function")
490
+ throw new Error("CanopyX: invalid export format.");return Object.freeze({id:format.id,label:format.label,extension:format.extension,mime:format.mime||"application/octet-stream",write:format.write})}
491
+ function registerExportFormat(format){format=exportFormat(format);EXPORT_FORMATS.set(format.id,format);return()=>EXPORT_FORMATS.get(format.id)===format&&EXPORT_FORMATS.delete(format.id)}
492
+ EXPORT_IMPL.formatsOf=function(grid){const list=grid.opts.exportFormats??grid.view.exportFormats,known=new Map(EXPORT_FORMATS);for(const format of grid.plugins.filter(item=>(grid.opts.plugins||[]).some(local=>local.name===item.name))
493
+ .flatMap(item=>item.exportFormats||[]))known.set(format.id,exportFormat(format));if(list===undefined)return[...known.values()];const formats=Array.isArray(list)?list.map(item=>typeof item==="string"?known.get(item):exportFormat(item)).filter(Boolean):null;if(!formats||new Set(formats.map(format=>format.id)).size!==formats.length)throw new Error("CanopyX: invalid exportFormats.");return formats};function exportTimeout(grid,options){const timeout=options?.timeoutMs??grid.opts.exportTimeoutMs??600000;if(!Number.isInteger(timeout)||timeout<1||timeout>2147483647)throw new Error("CanopyX: invalid export timeout.");return timeout}
494
+ Object.assign(TreeGrid.prototype,{buildToolbar(){const bar=this.toolbar,tools=this.tools;if(this.view.search&&this.source.canSearch&&tools.search!==false)this.buildSearch(bar,this.view.search);if(tools.filters!==false)(this.view.filters||[]).forEach((filter)=>{if(filter.hidden)return;const label=h("label","cx-switch");const input=h("input");input.type="checkbox";input.setAttribute("data-cx-filter",filter.id);input.addEventListener("change",()=>{const pending=this.setFilterActive(filter.id,input.checked);if(pending&&pending.catch)pending.catch(()=>{})});label.appendChild(input);label.appendChild(document.createTextNode(filter.label));bar.appendChild(label)});this.syncToolbar();this.toolsReady=loadPart(TREE_TOOLS).then(()=>this.destroyed||this.installTools()).catch(error=>this.emit("error",{error}))},syncToolbar(){this.toolbar.hidden=!this.toolbar.firstChild||this.opts.toolbar===false},syncColumnControls(){},clearDeclaredFilters(){if(!this.activeFilters.size){return}
457
495
  this.activeFilters.clear();this.toolbar.querySelectorAll("[data-cx-filter]").forEach((input)=>{input.checked=false});this.emit("filter",{active:[]});this.stateChanged()},buildSearch(bar,options){const box=h("div","cx-search");const input=h("input","cx-search-input");const list=h("ul","cx-results");const status=h("span","cx-sr-only");list.id=this.id+"-results";list.hidden=true;const labels=this.labels;const count=(value)=>numberFormat(this.lang).format(value);list.setAttribute("role","listbox");list.setAttribute("aria-label",labels.searchResults);input.type="search";input.placeholder=options.placeholder||labels.searchPlaceholder;input.setAttribute("aria-label",options.placeholder||labels.searchPlaceholder);input.setAttribute("role","combobox");input.setAttribute("aria-autocomplete","list");input.setAttribute("aria-expanded","false");input.setAttribute("aria-controls",list.id);status.setAttribute("aria-live","polite");let timer=0;let blurTimer=0;let sequence=0;let navigation=null;const cancelNavigation=()=>{if(navigation)navigation.abort()};let hits=[];let active=-1;const close=()=>{list.hidden=true;active=-1;input.setAttribute("aria-expanded","false");input.removeAttribute("aria-activedescendant")};const flyout=watchFlyout(box,list,()=>!list.hidden);this.disposeSearchFlyout=flyout.dispose;const choose=async(index)=>{const hit=hits[index];if(!hit){return}
458
496
  cancelNavigation();const current=navigation=new AbortController();close();try{await this.expandPath(hit.path,{select:true,focus:true,signal:current.signal})}catch(err){if(!this.destroyed&&!current.signal.aborted)setText(status,fill(labels.searchFailed,{error:err.message}))}};const show=(result)=>{hits=result.hits||[];list.textContent="";hits.forEach((hit,index)=>{const item=h("li","cx-result");item.id=list.id+"-"+index;item.setAttribute("role","option");item.setAttribute("aria-selected","false");const label=h("span","cx-result-label");setText(label,hit.label);item.appendChild(label);if(hit.kind){const kind=h("span","cx-result-kind");setText(kind,hit.kind);item.appendChild(kind)}
459
497
  if(hit.context){const context=h("span","cx-result-context");setText(context,hit.context);item.appendChild(context)}
@@ -464,19 +502,43 @@ fill(labels.searchFailed,{error:err&&err.message?err.message:err}))}}};const mov
464
502
  if(active>=0){list.children[active].setAttribute("aria-selected","false")}
465
503
  active=active<0?(delta<0?hits.length-1:0):(active+delta+hits.length)%hits.length;const item=list.children[active];item.setAttribute("aria-selected","true");input.setAttribute("aria-activedescendant",item.id);item.scrollIntoView({block:"nearest"})};input.addEventListener("input",()=>{cancelNavigation();sequence++;clearTimeout(timer);hits=[];close();timer=setTimeout(run,150)});input.addEventListener("keydown",(e)=>{if(e.defaultPrevented||e.isComposing)return;if(e.key==="ArrowDown"||e.key==="ArrowUp"){move(e.key==="ArrowDown"?1:-1)}else if(e.key==="Enter"){if(!list.hidden){choose(active>=0?active:0)}}else if(e.key==="Escape"){cancelNavigation();sequence++;clearTimeout(timer);if(list.hidden){input.value=""}
466
504
  close()}else{return}
467
- e.preventDefault()});input.addEventListener("blur",()=>{cancelNavigation();sequence++;clearTimeout(timer);clearTimeout(blurTimer);blurTimer=setTimeout(close,150)});this.cancelSearch=()=>{cancelNavigation();sequence++;clearTimeout(timer);clearTimeout(blurTimer);hits=[];close()};box.appendChild(input);box.appendChild(list);box.appendChild(status);bar.appendChild(box);this.searchInput=input},buildColumnChooser(bar){const details=h("details","cx-columns");const summary=h("summary");setText(summary,this.labels.columns);const group=h("fieldset","cx-columns-list");const legend=h("legend","cx-sr-only");setText(legend,this.labels.visibleColumns);group.appendChild(legend);this.columns.slice(1).forEach((column)=>{const label=h("label");const input=h("input");input.type="checkbox";input.checked=!column.hidden;input.setAttribute("data-cx-column",column.id);input.addEventListener("change",()=>this.setColumnVisible(column.id,input.checked));label.appendChild(input);label.appendChild(document.createTextNode(column.title));group.appendChild(label)});details.appendChild(summary);details.appendChild(group);bar.appendChild(details);const flyout=watchFlyout(details,group,()=>details.open);details.addEventListener("toggle",flyout.position);this.disposeColumnChooser=flyout.dispose}});function watchFlyout(anchorElement,panel,isOpen){const position=()=>{if(!isOpen())return;const anchor=anchorElement.getBoundingClientRect(),fixed=getComputedStyle(panel).position==="fixed";const below=Math.max(0,global.innerHeight-anchor.bottom-8);const above=Math.max(0,anchor.top-8);const opensAbove=below<120&&above>below,room=opensAbove?above:below;panel.style.maxHeight=(panel.classList.contains("cx-fb")?room:Math.min(320,room))+ "px";panel.style.top=opensAbove?"auto":fixed?anchor.bottom+"px":"100%";panel.style.bottom=opensAbove?fixed?global.innerHeight-anchor.top+"px":"100%":"auto";const width=panel.getBoundingClientRect().width;panel.style.left=Math.max(8,Math.min(anchor.left,global.innerWidth-width-8))-(fixed?0:anchor.left)+ "px"};global.addEventListener("resize",position);global.addEventListener("scroll",position,true);return{position,dispose:()=>{global.removeEventListener("resize",position);global.removeEventListener("scroll",position,true)}}}
468
- function readStateColumn(c,seen){requireIdentifier(c&&c.id,"state column ID");if(seen.has(c.id)||!Number.isFinite(c.width)||c.width<40||c.width>MAX_COLUMN_WIDTH||typeof c.hidden!=="boolean")
505
+ e.preventDefault()});input.addEventListener("blur",()=>{cancelNavigation();sequence++;clearTimeout(timer);clearTimeout(blurTimer);blurTimer=setTimeout(close,150)});this.cancelSearch=()=>{cancelNavigation();sequence++;clearTimeout(timer);clearTimeout(blurTimer);hits=[];close()};box.appendChild(input);box.appendChild(list);box.appendChild(status);bar.appendChild(box);this.searchInput=input}});function watchFlyout(anchorElement,panel,isOpen){const position=()=>{if(!isOpen())return;const anchor=anchorElement.getBoundingClientRect(),fixed=getComputedStyle(panel).position==="fixed";const below=Math.max(0,global.innerHeight-anchor.bottom-8);const above=Math.max(0,anchor.top-8);const opensAbove=below<120&&above>below,room=opensAbove?above:below;panel.style.maxHeight=(panel.classList.contains("cx-fb")?room:Math.min(320,room))+ "px";panel.style.top=opensAbove?"auto":fixed?anchor.bottom+"px":"100%";panel.style.bottom=opensAbove?fixed?global.innerHeight-anchor.top+"px":"100%":"auto";const width=panel.getBoundingClientRect().width;panel.style.left=Math.max(8,Math.min(anchor.left,global.innerWidth-width-8))-(fixed?0:anchor.left)+ "px"};global.addEventListener("resize",position);global.addEventListener("scroll",position,true);return{position,dispose:()=>{global.removeEventListener("resize",position);global.removeEventListener("scroll",position,true)}}}
506
+ const PART_URL=typeof document!=="undefined"&&document.currentScript?.src;const PARTS={"tree-tools":["ce442e100494535d","canopyx-tree-tools.149c371950d66a15.js","canopyx-tree-tools.ef7a3fb9c9115a82.css"],"filter-editor":["99cafeec3da59ffa","canopyx-filter-editor.7d9a82809044997b.js","canopyx-filter-editor.01b7bb6820dc9edb.css"],"export":["5673e642f32c2f4e","canopyx-export.01512af72a298711.js"],"history":["7d9901d2fe04df72","canopyx-history.9432e724bf14c9ed.js"],"pager":["8f804f70b96409f2","canopyx-pager.ac543250cf2f6e8b.js","canopyx-pager.08ad67c69026852e.css"],"enhance":["31162b1ffa622bf8","canopyx-enhance.65868addbc5c4a1a.js"],"data":["40fe19a1046d6813","canopyx-data.30d9664c5ef24288.js"],"editing":["01965774e28f70bf","canopyx-editing.71a336fd0766bf1d.js","canopyx-editing.06eef56a8785fe49.css"],"column-groups":["b42238fd108a8614","canopyx-column-groups.d8ad6322f769b915.js","canopyx-column-groups.37e177d74cebb516.css"],"cell-range":["e99a39e7cccc57b1","canopyx-cell-range.647da96e57924f3e.js","canopyx-cell-range.23f3222652588441.css"],"links":["f1ab7e32b501ce37","canopyx-links.d96addeefb3eccf5.js"],"search":["521eebd6e1bbbd35","canopyx-search.14c69f4551c69f23.js"],"formatting":["6f9f3c51b0ad2a0e","canopyx-formatting.83a489f9fe5ff09c.js","canopyx-formatting.a59507e4e8334891.css"],"format-editor":["24f176a39c02944a","canopyx-format-editor.3b1dec2527e53988.js","canopyx-format-editor.1ba153c82aa87b04.css"],"link":["fa0c9e888463553b","canopyx-link.4ec39bc39fa6cf50.js","canopyx-link.8435c892613926ac.css"],"detail-rows":["6c96974326d65a4c","canopyx-detail-rows.a267cf8361634006.js","canopyx-detail-rows.8e38a8ad01fded76.css"],"grouping":["d942108ffbc023b6","canopyx-grouping.1387fa5fa1374471.js","canopyx-grouping.6c93259b4b89e523.css"],"highlight":["f39423961654c1e2","canopyx-highlight.a6f93893d36ab343.js","canopyx-highlight.5b6d2e0d979f9f9b.css"],"layouts":["fa8c686f21626884","canopyx-layouts.e7250c82a6abf52e.js","canopyx-layouts.0b75738924533ee8.css"],"facets":["f9219d35a87635f8","canopyx-facets.8bf477b7915bdcd6.js","canopyx-facets.648a88d2f36d4999.css"],"live":["2fa02b9178c6c5df","canopyx-live.b53c60db00f9caf9.js","canopyx-live.cc4b94a2dec6f064.css"],"record-panel":["bcc06276c7933af6","canopyx-record-panel.a374fb4254f9115c.js","canopyx-record-panel.f3691d54cefaab04.css"],"calendar":["2f44e057443bc649","canopyx-calendar.9dda223f0fe4597e.js","canopyx-calendar.1099197a45632f63.css"],"calendar-recurrence":["64bcc24d785bd1af","canopyx-calendar-recurrence.4b7f430c2a64de23.js"],"calendar-edit":["bdde26d4b7de2096","canopyx-calendar-edit.4ab2cf55e84f310f.js"],"calendar-ics":["feb3ed2cd67d057f","canopyx-calendar-ics.2a3af9eb3bf444fc.js"],"timeline":["3f04ba99401e9ebd","canopyx-timeline.76928f089774b3de.js","canopyx-timeline.0393f92746f8fa09.css"]},PART_CODE=new Map(),partName=name=>typeof name==="string"&&/^[a-z][a-z\d-]{0,31}$/.test(name);
507
+ function registerPart(name,part){if(!partName(name)||!part||typeof part.version!=="string"||typeof part.install!=="function"||
508
+ part.requires!==undefined&&!(Array.isArray(part.requires)&&part.requires.every(partName)))throw new Error("CanopyX: invalid part.");PART_CODE.set(name,part)}
509
+ function definePart(View,name,label,context){const parts=View.prototype.parts||={};return parts[name]={name,label,context,parts}}
510
+ function loadPart(part){return part.ready||=Promise.resolve().then(async()=>{const[version,scriptFile,styleFile]=Object.hasOwn(PARTS,part.name)?PARTS[part.name]:[];part.version=version;if(!version)throw new Error("CanopyX: this runtime has no "+part.label+".");if(PART_CODE.get(part.name)?.version!==part.version||part.styleReady===null){const find=type=>[...document.querySelectorAll('script[type="application/x-canopyx-'+type+'"][data-cx-version="'+part.version+'"]')].find(item=>!pageNonce||item.nonce===pageNonce);const payload=find(part.name),script=h("script"),embeddedStyle=styleFile&&find(part.name+"-style");if(payload){if(embeddedStyle){const style=h("style");style.nonce=payload.nonce;setText(style,embeddedStyle.textContent);document.head.appendChild(style)}
511
+ script.textContent=trusted("Script",payload.textContent);script.nonce=payload.nonce;document.head.appendChild(script);script.remove()}else{const base=global.CanopyXFilterEditorBase,runtime=PART_URL||typeof base==="string"&&base||document.baseURI,hashed=PART_URL&&/\.[a-f\d]{16}\.js(?:[?#]|$)/.test(PART_URL);
512
+ const load=(element,attribute,file,kind)=>new Promise((resolve,reject)=>{const url=new URL(file,runtime).href;element[attribute]=attribute==="src"?trusted("ScriptURL",url):url;element.onload=resolve;element.onerror=()=>{element.remove();reject(new Error("CanopyX: "+part.label+" "+kind+" unavailable."))};document.head.appendChild(withNonce(element))});const link=h("link");link.rel="stylesheet";const style=styleFile&&(part.styleReady||=load(link,"href",hashed?styleFile:"canopyx-"+part.name+".css","stylesheet")
513
+ .catch(error=>{part.styleReady=null;throw error}));const code=PART_CODE.get(part.name)?.version===part.version||(part.scriptReady||=load(script,"src",hashed?scriptFile:"canopyx-"+part.name+".js","script")
514
+ .finally(()=>script.remove()).catch(error=>{part.scriptReady=null;throw error}));await Promise.all([style,code])}}
515
+ const code=PART_CODE.get(part.name);if(code?.version!==part.version)throw new Error("CanopyX: "+part.label+" and runtime must come from the same build.");for(const name of code.requires||[])await loadPart(part.parts[name]);code.install(part.context())}).catch(error=>{part.ready=null;throw error})}
516
+ const partQueue=global.CanopyXParts=global.CanopyXParts||[];partQueue.forEach(entry=>registerPart(...entry));partQueue.length=0;partQueue.push=(...entries)=>{entries.forEach(entry=>registerPart(...entry));return 0};const TREE_TOOLS=definePart(TreeGrid,"tree-tools","tree tools",()=>({TreeGrid,h,setText,fill,numberFormat,watchFlyout,columnWidth,prepareWidth,cellText,cellValue,isCellObject,foldText,formatting,sortKey,compareKeys,EXPORT_FORMATS,exportFormat,exportTimeout,exportFormatsOf:EXPORT_IMPL.formatsOf,INDENT,MAX_PROTOCOL_INTEGER,hostControl,detailCell,HostCellContext,disposeHostSlot,loadPart,definePart,pluginItems,applyTone,toneOf,badgeElement,RECORD_IMPL,svgIcon,gridExportField}));const SEARCH_IMPL={},SEARCH_PART=definePart(TreeGrid,"search","sidecar search",()=>({SEARCH_IMPL,checkDocument,badIndex,isCount,isIdentifier,recentCache,packBytes,shardKey,foldText,trimSeparators,rankHits,validateSearchHit,PACK_CACHE,PACK_CACHE_BYTES,SHARD_CACHE,ENTRY_CACHE,ENTRY_BATCH,SEARCH_CANDIDATES}));async function requirePart(name){await this.toolsReady;const part=Object.hasOwn(this.parts,name)?this.parts[name]:null;if(!part)throw new Error("CanopyX: unknown part '"+name+"'.");return loadPart(part)}
517
+ TreeGrid.prototype.requirePart=requirePart;function readStateColumn(c,seen){requireIdentifier(c&&c.id,"state column ID");if(seen.has(c.id)||!Number.isFinite(c.width)||c.width<40||c.width>MAX_COLUMN_WIDTH||typeof c.hidden!=="boolean")
469
518
  throw new Error("CanopyX: invalid state column.");seen.add(c.id);return{id:c.id,width:c.width,hidden:c.hidden}}
470
519
  function statePaths(value){if(!Array.isArray(value))throw new Error("CanopyX: state paths must be arrays.");return value.map(path=>{if(!Array.isArray(path)||!path.length)throw new Error("CanopyX: state paths cannot be empty.");return path.map(id=>{requireIdentifier(id,"state path ID");return id})})}
471
- Object.assign(TreeGrid.prototype,{getState(){const anchor=this.captureScrollAnchor(),anchorNode=anchor&&this.keyMap.get(anchor.id);return{version:1,expanded:this.getExpandedPaths(),selection:Array.from(this.selected,w=>this.pathOf(w)),columns:this.columns.map(c=>({id:c.id,width:c.width,hidden:!!c.hidden})),sort:this.sortState&&Object.assign({},this.sortState),filters:Array.from(this.activeFilters),timeZone:this.local?"local":"utc",pinTreeColumn:this.host.classList.contains("cx-pin-tree"),focus:this.focused&&!this.focused.status&&!this.focused._invalidated?this.pathOf(this.focused):null,anchor:anchorNode?{path:this.pathOf(anchorNode),offset:anchor.offset}:null}},readState(state){if(!state||typeof state!=="object")return null;try{const result={expanded:statePaths(state.expanded)};if(state.version===undefined)return result;if(state.version!==1)return null;result.version=1;result.selection=statePaths(state.selection);if(!this.multi&&result.selection.length>1)return null;if(!Array.isArray(state.columns)||!Array.isArray(state.filters))return null;const seen=new Set();result.columns=state.columns.map(c=>readStateColumn(c,seen));if(result.columns.some(c=>c.id===this.columns[0].id&&c.hidden))return null;result.filters=state.filters.map(id=>{requireIdentifier(id,"state filter ID");return id})
520
+ Object.assign(TreeGrid.prototype,{getState(){const anchor=this.captureScrollAnchor(),anchorNode=anchor&&this.keyMap.get(anchor.id);return{version:1,expanded:this.getExpandedPaths(),selection:Array.from(this.selected,w=>this.pathOf(w)),columns:this.columns.map(c=>({id:c.id,width:c.width,hidden:!!c.hidden})),sort:this.sortState&&Object.assign({},this.sortState),filters:Array.from(this.activeFilters),timeZone:this.local?"local":"utc",pinTreeColumn:this.host.classList.contains("cx-pin-tree"),focus:this.focused&&!this.focused.status&&!this.focused._invalidated?this.pathOf(this.focused):null,anchor:anchorNode?{path:this.pathOf(anchorNode),offset:anchor.offset}:null,...this.modeState({})}},readState(state){if(!state||typeof state!=="object")return null;try{const result={expanded:statePaths(state.expanded)};if(state.version===undefined)return result;if(state.version!==1)return null;result.version=1;result.selection=statePaths(state.selection);if(!this.multi&&result.selection.length>1)return null;if(!Array.isArray(state.columns)||!Array.isArray(state.filters))return null;const seen=new Set();result.columns=state.columns.map(c=>readStateColumn(c,seen));if(result.columns.some(c=>c.id===this.columns[0].id&&c.hidden))return null;result.filters=state.filters.map(id=>{requireIdentifier(id,"state filter ID");return id})
472
521
  .filter(id=>(this.view.filters||[]).some(f=>f.id===id));result.sort=null;if(state.sort!=null){requireIdentifier(state.sort.column,"state sort column");if(state.sort.direction!=="asc"&&state.sort.direction!=="desc")return null;if(this.columns.some(c=>c.id===state.sort.column))result.sort={column:state.sort.column,direction:state.sort.direction}}
473
522
  if(state.timeZone!=="utc"&&state.timeZone!=="local"||typeof state.pinTreeColumn!=="boolean")return null;result.timeZone=state.timeZone;result.pinTreeColumn=state.pinTreeColumn;result.focus=state.focus==null?null:statePaths([state.focus])[0];result.anchor=null;if(state.anchor!=null){if(!Number.isFinite(state.anchor.offset)||state.anchor.offset<0||state.anchor.offset>MAX_PROTOCOL_INTEGER)return null;result.anchor={path:statePaths([state.anchor.path])[0],offset:state.anchor.offset}}
474
- return result}catch(error){return null}},applyStatePresentation(state){if(!state.version)return;if(this.cancelResize)this.cancelResize();for(const saved of state.columns){const column=this.columns.find(c=>c.id===saved.id);if(column){column.width=columnWidth(column,saved.width);column.hidden=saved.hidden}}
475
- this.toolbar.querySelectorAll("[data-cx-column]").forEach(input=>{const column=this.columns.find(c=>c.id===input.getAttribute("data-cx-column"));if(column)input.checked=!column.hidden});this.sortState=state.sort;this.inlineOrderVersion=(this.inlineOrderVersion||0)+ 1;this.setTreeColumnPinned(state.pinTreeColumn);this.setTimeZone(state.timeZone);this.buildHeader();this.applySort(this.root,true);if(!this.queryAware)this.setActiveFilters(state.filters)},async loadStatePaths(state,current){const paths=state.selection.concat(state.focus?[state.focus]:[],state.anchor?[state.anchor.path]:[]);const attached=new Set([state.focus,state.anchor&&state.anchor.path].filter(Boolean).map(path=>JSON.stringify(path)));for(const path of paths){if(this.destroyed||current.signal.aborted)return;const existing=this.keyMap.get(path[path.length-1]);let cached=false;for(let node=existing;node;node=node.parent)if(node._cached)cached=true;if(existing&&JSON.stringify(this.pathOf(existing))===JSON.stringify(path)&&
523
+ Object.assign(result,readModeState(this,state));return result}catch(error){return null}},applyStatePresentation(state){if(!state.version)return;if(this.cancelResize)this.cancelResize();for(const saved of state.columns){const column=this.columns.find(c=>c.id===saved.id);if(column){column.width=columnWidth(column,saved.width);column.hidden=saved.hidden}}
524
+ this.syncColumnControls();this.sortState=state.sort;this.inlineOrderVersion=(this.inlineOrderVersion||0)+ 1;this.setTreeColumnPinned(state.pinTreeColumn);this.setTimeZone(state.timeZone);this.buildHeader();this.applySort(this.root,true);if(!this.queryAware)this.setActiveFilters(state.filters);this.applyModeState(state)},async loadStatePaths(state,current){const paths=state.selection.concat(state.focus?[state.focus]:[],state.anchor?[state.anchor.path]:[]);const attached=new Set([state.focus,state.anchor&&state.anchor.path].filter(Boolean).map(path=>JSON.stringify(path)));for(const path of paths){if(this.destroyed||current.signal.aborted)return;const existing=this.keyMap.get(path[path.length-1]);let cached=false;for(let node=existing;node;node=node.parent)if(node._cached)cached=true;if(existing&&JSON.stringify(this.pathOf(existing))===JSON.stringify(path)&&
476
525
  (!attached.has(JSON.stringify(path))||!cached))continue;try{await this.expandPath(path,{select:false,focus:false,reveal:false,signal:current.signal})}
477
526
  catch(error){/* Nodes removed since the snapshot are skipped. */ }
478
- }},applyStatePosition(state,current){if(!state.version)return;const valid=path=>{const w=path&&this.keyMap.get(path[path.length-1]);return w&&JSON.stringify(this.pathOf(w))===JSON.stringify(path)?w:null};this.setSelection(state.selection.map(valid).filter(Boolean).map(w=>w.key));if(this.destroyed||current.signal.aborted)return;const focused=valid(state.focus);if(focused&&this.indexOf(focused)>=0)this.setFocus(focused,false);const anchor=state.anchor&&valid(state.anchor.path);if(anchor)this.restoreScrollAnchor({id:anchor.key,offset:state.anchor.offset})}});function escapeHtml(value){return String(value==null?"":value).replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", "\"": "&quot;", "'": "&#39;" }[c]));
479
- }
527
+ }},applyStatePosition(state,current){if(!state.version)return;const valid=path=>{const w=path&&this.keyMap.get(path[path.length-1]);return w&&JSON.stringify(this.pathOf(w))===JSON.stringify(path)?w:null};this.setSelection(state.selection.map(valid).filter(Boolean).map(w=>w.key));if(this.destroyed||current.signal.aborted)return;const focused=valid(state.focus);if(focused&&this.indexOf(focused)>=0)this.setFocus(focused,false);const anchor=state.anchor&&valid(state.anchor.path);if(anchor)this.restoreScrollAnchor({id:anchor.key,offset:state.anchor.offset})}});const PLUGINS=new Map();function plugin(value){const item=Object.freeze({...value}),fn=handler=>typeof handler==="function";const entry=menu=>action=>action&&typeof action.label==="string"&&
528
+ (fn(action.run)&&action.render===undefined||menu&&fn(action.render)&&action.run===undefined);if(!value||!partName(item.name)||Object.hasOwn(PARTS,item.name)||[item.init,item.destroy].some(hook=>hook!==undefined&&!fn(hook))||
529
+ ["requires","views","toolbarItems","menuItems","exportFormats","recordActions"].some(key=>item[key]!==undefined&&!Array.isArray(item[key]))||
530
+ ["cellRenderers","columnTypes","editors","events","aggregates","detailRenderers","drawerSections"].some(key=>item[key]!==undefined&&Object(item[key])!==item[key])||
531
+ !(item.requires||[]).every(partName)||!(item.views||[]).every(kind=>kind==="tree"||kind==="grid")||
532
+ ![...Object.values(item.cellRenderers||{}),...Object.values(item.events||{}),...Object.values(item.detailRenderers||{}),...Object.values(item.drawerSections||{})].every(fn)||
533
+ !(item.toolbarItems||[]).every(entry())||!(item.recordActions||[]).every(entry())||!(item.menuItems||[]).every(entry(true)))
534
+ throw new Error("CanopyX: invalid plugin '"+item.name+"'.");item.exportFormats?.forEach(exportFormat);return item}
535
+ function use(value){const item=plugin(value);if(PLUGINS.has(item.name))throw new Error("CanopyX: plugin '"+item.name+"' is already registered.");PLUGINS.set(item.name,item);const formats=item.exportFormats?.map(registerExportFormat)||[];return()=>{if(PLUGINS.get(item.name)===item){PLUGINS.delete(item.name);formats.forEach(dispose=>dispose())}}}
536
+ function viewPlugins(options,kind){const byName=new Map(PLUGINS),ordered=[],visiting=new Set(),applies=item=>!item.views||item.views.includes(kind);for(const value of options.plugins||[]){const item=plugin(value);byName.delete(item.name);byName.set(item.name,item)}
537
+ const visit=item=>{if(ordered.includes(item)||!applies(item))return;if(visiting.has(item))throw new Error("CanopyX: plugin '"+item.name+"' requires itself.");visiting.add(item);for(const name of item.requires||[]){const required=byName.get(name);if(required&&applies(required))visit(required);else if(!Object.hasOwn(PARTS,name))throw new Error("CanopyX: plugin '"+item.name+"' requires '"+name+"', which is neither a plugin nor a part.")}
538
+ ordered.push(item)};byName.forEach(visit);return ordered}
539
+ function startPlugins(grid,api){if(!grid.plugins.length)return grid.ready;const signal=grid.lifetime.signal,failed=new Set(),byName=new Map(grid.plugins.map(item=>[item.name,item]));const blocked=item=>failed.has(item.name)||(item.requires||[]).some(name=>byName.has(name)&&blocked(byName.get(name)));const contexts=grid.plugins.map(item=>{const context=Object.freeze({get api(){return api()},element:grid.host,view:grid.view,kind:grid.kind,labels:grid.labels,signal,part:name=>grid.requirePart(name)});for(const[name,handler]of Object.entries(item.events||{}))grid.host.addEventListener("canopyx:"+name,event=>{if(!blocked(item))handler(event,context)},{signal});if(item.destroy)signal.addEventListener("abort",()=>item.destroy(context),{once:true});return context});const started=Promise.resolve().then(async()=>{for(const[index,item]of grid.plugins.entries()){if(signal.aborted)return;try{for(const name of item.requires||[]){if(failed.has(name))throw new Error("CanopyX: plugin '"+item.name+"' requires failed plugin '"+name+"'.");if(!grid.plugins.some(other=>other.name===name))await grid.requirePart(name)}
540
+ if(!signal.aborted){const context=contexts[index];await item.init?.(context)}}catch(error){failed.add(item.name);grid.emit("error",{error})}}});return Promise.all([grid.ready,started]).then(([result])=>result)}
541
+ function pluginItems(grid,key){return grid.plugins.flatMap(item=>item[key]||[])}
480
542
  Object.assign(TreeGrid.prototype,{pathOf(w){const path=[];for(let node=w;node&&node!==this.root;node=node.parent){path.push(node.key)}
481
543
  return path.reverse()},getExpandedPaths(){const out=[];const pending=this.modelChildren(this.root).slice().reverse();while(pending.length){const child=pending.pop();if(child.expanded&&this.expandable(child)){out.push(this.pathOf(child));const children=this.modelChildren(child);for(let i=children.length-1;i>=0;i--)pending.push(children[i])}}
482
544
  return out},async setState(state){if(this.destroyed)return false;state=this.readState(state);if(!state)return false;if(this.restoreController)this.restoreController.abort();const current=this.restoreController=new AbortController();this.restoreGeneration=(this.restoreGeneration||0)+ 1;this.pendingRestorationToggles=null;this.restoring=true;clearTimeout(this.saveTimer);const before=new Map();this.keyMap.forEach((w)=>before.set(w,[w.expanded,w.autoExpand,w.filterCollapsed]));let restored=0;let fallback=false;const release=this.holdLayout();try{if(this.queryAware&&state.version&&!await this.setQuery({version:1,sort:state.sort,filters:state.filters},{signal:current.signal},current))return false;if(this.destroyed||current.signal.aborted)return false;this.applyStatePresentation(state);if(state.version)await this.loadStatePaths(state,current);if(this.destroyed||current.signal.aborted)return false;this.keyMap.forEach((w)=>{w.expanded=false;w.autoExpand=false});this.relayout();const paths=state.expanded.slice().sort((a,b)=>a.length-b.length);for(const path of paths){if(this.destroyed||current.signal.aborted)break;try{const node=await this.expandPath(path,{select:false,focus:false,reveal:false,expandLast:true,signal:current.signal});const wrapper=node&&this.keyMap.get(node.id);if(wrapper&&wrapper.expanded)restored++}catch(e){const last=this.keyMap.get(path[path.length-1]);if((last&&last.expanded)||path.some((id)=>{const node=this.keyMap.get(id);return node&&node.expanded&&node.error}))restored++}}
@@ -490,10 +552,7 @@ catch(error){if(!failure)failure=error}}
490
552
  if(failure&&generation===(this.restoreGeneration||0))throw failure})();this.pendingToggleFlush=run;try{await run}finally{if(this.pendingToggleFlush===run)this.pendingToggleFlush=null;this.replayingToggles=false;this.cacheDirty=true;this.scheduleRender();if(this.pendingRestorationToggles===pending&&!pending.size){this.pendingRestorationToggles=null;if(!this.destroyed)this.stateChanged()}}},stateKey(){return this.view.stateKey==null?null:"canopyx:"+this.view.stateKey},stateChanged(){const key=this.stateKey();if(this.destroyed||!key||this.restoring||this.initializing||this.replayingToggles){return}
491
553
  clearTimeout(this.saveTimer);this.saveTimer=setTimeout(()=>{if(this.destroyed||this.restoring||this.initializing||this.replayingToggles)return;try{sessionStorage.setItem(key,JSON.stringify(this.getState()))}catch(e){}},250)},async restoreState(){const key=this.stateKey();if(!key){return}
492
554
  let state=null;try{state=JSON.parse(sessionStorage.getItem(key)||"null")}catch(e){state=null}
493
- await this.setState(state)},visibleRows(){return this.rows.filter((w)=>!w.status).map((w)=>({node:w.h,level:w.level,path:this.pathOf(w)}))},toOutline(){const cols=this.visibleColumns();const out=["<table class=\"cx-outline-table\"><thead><tr>"];cols.forEach((column)=>out.push("<th>"+escapeHtml(column.title)+ "</th>"));out.push("</tr></thead><tbody>");this.rows.forEach((w)=>{if(w.status){return}
494
- const pad=" style=\"padding-left:" +((w.level-1)*14+4)+ "px\"";if(w.h.detail){const detail=w.h.detail;const content=h("div");this.renderDetail(w.h,content,true);out.push("<tr><td"+pad+" colspan=\""+cols.length+"\">"+content.innerHTML+"</td></tr>");return}
495
- out.push("<tr>");cols.forEach((column,i)=>{const index=this.columns.indexOf(column);const value=this.cellOf(w.h,column,index);const text=cellText(value,column,this.fmt);const content=h("span");const slot={};try{if(!column.render||column.render(w.h,content,new HostCellContext(slot,column.id,w.level,w.expanded,index))!==true){const badges=(i===0?w.h.badges||[]:[]).concat(isCellObject(value)?value.badges||[]:[]);content.appendChild(detailCell("span",Object.assign({},isCellObject(value)?value:{},{text,badges}),this.fmt))}
496
- out.push("<td" +(i===0?pad:"")+ ">"+content.outerHTML+"</td>")}finally{disposeHostSlot(slot)}});out.push("</tr>")});out.push("</tbody></table>");return out.join("")},invalidateChildren(w){const pending=this.modelChildren(w).slice();let removedSelection=null;while(pending.length){const child=pending.pop();for(const descendant of this.modelChildren(child))pending.push(descendant);const removed=this.retireWrapper(child);if(!removedSelection)removedSelection=removed}
555
+ await this.setState(state)},visibleRows(){return this.rows.filter((w)=>!w.status).map((w)=>({node:w.h,level:w.level,path:this.pathOf(w)}))},toOutline(){return this.outlineElement()?.outerHTML||""},outlineElement(){return this.outlineTable?this.outlineTable():null},invalidateChildren(w){const pending=this.modelChildren(w).slice();let removedSelection=null;while(pending.length){const child=pending.pop();for(const descendant of this.modelChildren(child))pending.push(descendant);const removed=this.retireWrapper(child);if(!removedSelection)removedSelection=removed}
497
556
  w.children=null;w.inlineCache=null;w.inlineOrder=null;if(this.inactiveCache)this.inactiveCache.delete(w);w.next=null;w.error=null;w.expanded=false;return removedSelection},applyCounts(doc,fromRefresh=false){if(this.destroyed)return;checkDocument(doc,"counts");if(this.queryAware)snapshotRevision(doc,this.datasetRevision);let removedSelection=null;let removed=false;doc.items.forEach((item)=>{const w=this.keyMap.get(item.id);if(w&&w.inline&&item.childCount!=null&&item.childCount!==w.h.children.length){throw new Error("CanopyX: inline childCount must equal children length.")}});if(!fromRefresh){for(const id of new Set(doc.items.map((item)=>item.id))){const w=this.keyMap.get(id);if(w)w.countRevision=(w.countRevision||0)+ 1}}
498
557
  doc.items.forEach((item)=>{const w=this.keyMap.get(item.id);if(!w){return}
499
558
  if(item.cells)w._v=0;if(typeof item.childCount==="number"){w.h.childCount=item.childCount;w.total=item.childCount;if(!w.inline){w.h.hasChildren=item.childCount>0;const loaded=w.children?w.children.length:0;if(w.loading)w.pendingChildCount=item.childCount;else if(item.childCount<loaded||(item.childCount>loaded&&w.children&&!w.next)){removedSelection=this.invalidateChildren(w)||removedSelection;removed=true}
@@ -505,30 +564,48 @@ w.content=nodeContent(w.h)});this.inlineOrderVersion=(this.inlineOrderVersion||0
505
564
  const wanted=Array.from(new Set(ids||this.rows.filter((w)=>!w.status).map((w)=>w.key)));const mine=new Map();for(const id of wanted){const w=this.keyMap.get(id);if(w){w.countRevision=(w.countRevision||0)+ 1;mine.set(id,{w,revision:w.countRevision})}}
506
565
  const navigation=navigationScope(this,this.dataLifetime.signal);let doc;try{doc=await navigation.wait(this.source.counts(wanted,this.sourceRequest(this.dataLifetime.signal)))}
507
566
  finally{navigation.dispose()}
508
- if(navigation.cancelled())return null;if(this.queryAware)snapshotRevision(doc,this.datasetRevision);checkDocument(doc,"counts");this.applyCounts(Object.assign({},doc,{items:doc.items.filter(item=>{const captured=mine.get(item.id);return captured&&this.keyMap.get(item.id)===captured.w&&captured.w.countRevision===captured.revision})}),true);return doc}});Object.assign(TreeGrid.prototype,{deepLinkName(){return "cx."+encodeURIComponent(this.view.id)},hashParams(){return(global.location.hash||"").replace(/^#/, "").split("&").filter(Boolean);
509
- },currentLink(){if(this.opts.parseLink){return(global.location.hash||"").replace(/^#/, "");
510
- }
511
- const prefix=this.deepLinkName()+ "=";const param=this.hashParams().find((p)=>p.startsWith(prefix));return param?param.slice(prefix.length):null},readLocation(){if(this.destroyed)return Promise.resolve();if(this.initializing){if(this.linkOpening&&this.currentLink()!==this.link){this.linkOpening.abort();this.link=null;// Deferred reopening must not reuse the canceled link if the address changes back.
567
+ if(navigation.cancelled())return null;if(this.queryAware)snapshotRevision(doc,this.datasetRevision);checkDocument(doc,"counts");this.applyCounts(Object.assign({},doc,{items:doc.items.filter(item=>{const captured=mine.get(item.id);return captured&&this.keyMap.get(item.id)===captured.w&&captured.w.countRevision===captured.revision})}),true);return doc}});const LINKS=definePart(TreeGrid,"links","deep links",()=>({TreeGrid,navigationScope}));for(const name of["openDeepLink","writeDeepLink"])TreeGrid.prototype[name]=function(...args){return this.view.deepLink?loadPart(LINKS).then(()=>this[name](...args),error=>this.emit("error",{error})):Promise.resolve()};TreeGrid.prototype.readLocation=function(){if(this.destroyed)return Promise.resolve();if(this.initializing){if(this.linkOpening&&this.currentLink()!==this.link){this.linkOpening.abort();this.link=null;// Deferred reopening must not reuse the canceled link if the address changes back.
512
568
  }
513
569
  this.linkChanged=this.dataLifetime.signal;return this.ready.then(()=>undefined,()=>undefined)}
514
- return this.openDeepLink()},openDeepLink(){const link=this.view.deepLink?this.currentLink():null;if(link!==this.link&&this.linkOpening)this.linkOpening.abort();if(link==null||link===this.link){if(link==null)this.link=null;return link!=null&&this.linkTask&&this.linkTask.link===link?this.linkTask.promise:Promise.resolve()}
515
- this.link=link;if(this.linkOpening)this.linkOpening.abort();const current=this.linkOpening=new AbortController();const promise=this.openLink(link,current);this.linkTask={link,promise};promise.then(()=>{if(this.linkTask&&this.linkTask.promise===promise)this.linkTask=null});return promise},async openLink(link,current){const navigation=navigationScope(this,current.signal,true);try{const ids=this.opts.parseLink?await navigation.wait(this.opts.parseLink(link)):link.split("/").filter(Boolean).map(decodeURIComponent);if(Array.isArray(ids)&&ids.length&&!navigation.cancelled()&&
516
- !await this.navigatePath(ids,{select:true,focus:false},navigation)&&!navigation.cancelled()){this.link=null}}catch(e){if(!navigation.cancelled()){this.link=null;this.announce(e&&e.message?e.message:String(e))}}finally{const cancelled=navigation.cancelled();navigation.dispose();if(this.linkOpening===current){if(cancelled&&this.link===link)this.link=null;this.linkOpening=null}
517
- current.abort()}},writeDeepLink(w,revision=this.selectionRevision){if(!this.view.deepLink){return}
518
- let hash;if(this.opts.formatLink){let link=null;try{link=this.opts.formatLink(w?this.pathOf(w):[],w?w.h:null)}catch(e){if(global.console)global.console.error(e)}
519
- if(link==null||this.destroyed||revision!==this.selectionRevision){return}
520
- hash="#"+String(link).replace(/^#/, "");
521
- }else{const prefix=this.deepLinkName()+ "=";const params=this.hashParams().filter((p)=>!p.startsWith(prefix));if(w)params.push(prefix+this.pathOf(w).map(encodeURIComponent).join("/"));hash="#"+params.join("&")}
522
- try{global.history.replaceState(global.history.state,"",global.location.href.replace(/#.*$/, "") + hash);
523
- }catch(e){global.location.hash=hash}
524
- this.link=this.currentLink();this.locationEvent=null}});Object.assign(TreeGrid.prototype,{destroy(){if(this.destroyed)return;this.destroyed=true;for(const row of[...this.rendered.values(),...this.pool])disposeRowHostCells(row);this.lifetime.abort();this.dataLifetime.abort();if(this.queryController)this.queryController.abort();if(this.restoreController)this.restoreController.abort();if(this.cancelResize)this.cancelResize();clearTimeout(this.saveTimer);clearTimeout(this.layoutTimer);if(this.cancelSearch)this.cancelSearch();if(this.disposeColumnChooser)this.disposeColumnChooser();if(this.disposeSearchFlyout)this.disposeSearchFlyout();cancelAnimationFrame(this.rafId);this.resizeObserver.disconnect();if(this.detailObserver)this.detailObserver.disconnect();this.grid.removeEventListener("scroll",this.onScroll);this.grid.removeEventListener("wheel",this.onScaledWheel);this.grid.removeEventListener("touchstart",this.onScaledTouchStart);this.grid.removeEventListener("touchmove",this.onScaledTouchMove);this.grid.removeEventListener("touchend",this.onScaledTouchEnd);this.grid.removeEventListener("touchcancel",this.onScaledTouchEnd);global.removeEventListener("beforeprint",this.onPrint);global.removeEventListener("hashchange",this.onHash);global.removeEventListener("popstate",this.onHash);if(this.linkOpening)this.linkOpening.abort();[this.toolbar,this.grid,this.live,this.outline].forEach(element=>element.remove());this.host.classList.remove("cx-pin-tree");this.host.removeAttribute("data-cx-ready");this.host.removeAttribute("data-canopyx-mounted");this.keyMap.clear();this.selected.clear();this.rendered.clear();this.pool.length=0;if(this.inactiveCache)this.inactiveCache.clear();if(this.inlineRetained)this.inlineRetained.clear();this.rows=[];this.tops=[0];this.total=0;this.root.children=null;this.root.inlineCache=null;this.root.inlineOrder=null;this.focused=null;this.anchor=null;this.source=null;this.touchScroll=null;this.opts={};for(const column of this.columns)column.render=null;this.filter=null;this.declaredFilter=null;this.pendingRestorationToggles=null;this.scrolledCell=null;this.focusHandoff=null;this.cancelResize=null;this.cancelSearch=null;this.disposeColumnChooser=null;this.disposeSearchFlyout=null;this.canvas.replaceChildren();this.outline.replaceChildren()}});const ROW_HELPERS={};const GRID_REPORTING={};function gridFreeze(value){if(value&&typeof value==="object"&&!Object.isFrozen(value)){Object.values(value).forEach(gridFreeze);Object.freeze(value)}
570
+ return this.openDeepLink()};DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{linkFilteredBy:"Filtered by {source}: {values}",linkRemoveFilter:"Remove the filter from {source}: {values}",linkFiltered:"Filtered by {source}: {count} rows",linkHighlighted:"{count} rows highlighted from {source}",linkHighlightedRows:"{count} rows highlighted",linkCleared:"Linked filters removed"}));const LINK_IMPL={TreeGrid,queryOwn:Symbol("own conditions")},LINK_PART=definePart(TreeGrid,"link","linked views",()=>({LINK_IMPL,h,setText,filterChipContent,fill,numberFormat,cellValue,setAttr,cellMatches}));const LINK_METHODS=["setExternalFilter","clearExternalFilters","setHighlight","clearHighlight","startLinkEvents"];function linkMethods(View){for(const name of LINK_METHODS)(View.prototype[name]=function(...args){return this.destroyed?Promise.resolve(/Highlight/.test(name) ? 0 : name === "startLinkEvents" ? undefined : false) : loadPart(LINK_PART).then(() => this[name](...args));
571
+ }).linkStub=true;View.prototype.getExternalFilters=function(){return(this.linkCommitted||[]).map(item=>JSON.parse(JSON.stringify(item)))};View.prototype.getHighlight=function(){return Array.from(this.linkHighlight||[])};View.prototype.linkStart=function(){if(this.opts.linkEvents===true)return loadPart(LINK_PART).then(()=>this.linkWatch()).catch(error=>{this.emit("error",{error});throw error})}}
572
+ linkMethods(TreeGrid);function linkGridPart(View){(Object.hasOwn(View.prototype,"parts")?View.prototype.parts:View.prototype.parts={}).link=LINK_PART}
573
+ function linkReady(grid,ready){return Promise.all([ready,grid.linkStart()]).then(([result])=>result)}
574
+ function linkApi(grid){const api={};for(const name of LINK_METHODS.concat("getExternalFilters","getHighlight"))api[name]=(...args)=>grid[name](...args);return api}
575
+ const RECORD_IMPL={},RECORD_PART=definePart(TreeGrid,"record-panel","record panel",()=>({RECORD_IMPL,TreeGrid,h,setText,fill,cellText,cellValue,isCellObject,formatValue,toneOf,applyTone,toneColors,badgeElement,loadSidecar,requireIdentifier,validateGridRows,PROTOCOL,foldText,createTreeGrid,svgIcon,gridExportField}));function loadRecordPanel(){return loadPart(RECORD_PART)}
576
+ const RECORD_STRUCTURE=["items","fields","records","events","changes","ref"];TreeGrid.prototype.recordNeeded=function(node){return!!this.view.record||!!node&&(!!node.fields||Object.values(node.cells||{}).some(cell=>isCellObject(cell)&&RECORD_STRUCTURE.some(name=>cell[name]!==undefined)))};TreeGrid.prototype.openRecord=function(id,options){return loadRecordPanel().then(()=>RECORD_IMPL.open(this,id,options))};function recordStart(grid){if(grid.view.record?.deepLink)loadRecordPanel().then(()=>RECORD_IMPL.start(grid),error=>grid.emit("error",{error}))}
577
+ DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{matchesHighlight:"Highlight matches",matchesCount:"[one]{count} match[other]{count} matches",matchesPosition:"{current} of {count}",matchesNone:"No matches",matchesLoaded:"[one]{count} match in loaded rows[other]{count} matches in loaded rows",matchesPrevious:"Previous match",matchesNext:"Next match",matchesAnnounce:"Match {current} of {count}, {column}",layoutMenu:"Layout",layoutFit:"Fit to width",layoutScroll:"All columns, scroll sideways",layoutCards:"Cards",layoutDensity:"Density",layoutCompact:"Compact",layoutNormal:"Normal",layoutComfortable:"Comfortable",layoutChanged:"Layout: {mode}, {density}",layoutSort:"Sort by"}));const LAYOUT_MODES=["fit","scroll","cards"],LAYOUT_DENSITIES=["compact","default","comfortable"];const DENSITY_STEP={tree:{compact:-4,default:0,comfortable:8},grid:{compact:-6,default:0,comfortable:8}};const VIEW_MODES={};const HIGHLIGHT_KEYS=["enabled","search","filters","matchCase","ignoreAccents","minLength","filterTones"];function readSearchHighlight(value,columns){if(value===undefined||value===false)return null;const options=value===true?{}:value,tone=name=>typeof name==="string"&&/^[a-z][a-z0-9-]{0,31}$/.test(name) && !name.endsWith("-bg");
578
+ if(!options||typeof options!=="object"||Array.isArray(options)||Object.keys(options).some(key=>!HIGHLIGHT_KEYS.includes(key))||
579
+ HIGHLIGHT_KEYS.slice(0,5).some(key=>options[key]!==undefined&&typeof options[key]!=="boolean")||
580
+ options.minLength!==undefined&&(!Number.isInteger(options.minLength)||options.minLength<1||options.minLength>32)||
581
+ options.filterTones!==undefined&&(!options.filterTones||typeof options.filterTones!=="object"||Array.isArray(options.filterTones)||
582
+ Object.entries(options.filterTones).some(([id,name])=>!columns.some(column=>column.id===id)||!tone(name))))
583
+ throw new Error("CanopyX: invalid search highlight options.");return Object.freeze({enabled:options.enabled!==false,search:options.search!==false,filters:options.filters!==false,matchCase:!!options.matchCase,ignoreAccents:!!options.ignoreAccents,minLength:options.minLength||1,filterTones:Object.freeze({...options.filterTones})})}
584
+ function readLayout(value,kind,density){if(value===undefined)return null;const all=kind==="tree"?LAYOUT_MODES.slice(0,2):LAYOUT_MODES;if(!value||typeof value!=="object"||Array.isArray(value)||Object.keys(value).some(key=>!["mode","modes","density"].includes(key))||
585
+ value.modes!==undefined&&(!Array.isArray(value.modes)||!value.modes.length||new Set(value.modes).size!==value.modes.length||
586
+ value.modes.some(mode=>!all.includes(mode)))||
587
+ value.mode!==undefined&&!(value.modes||all).includes(value.mode)||value.density!==undefined&&!LAYOUT_DENSITIES.includes(value.density))
588
+ throw new Error("CanopyX: invalid layout options.");const modes=Object.freeze((value.modes||all).slice());return Object.freeze({mode:value.mode||(modes.includes("fit")?"fit":modes[0]),modes,density:value.density||density||"default"})}
589
+ function readModeState(view,state){const result={};if(state.layout!==undefined){const layout=state.layout,options=view.layoutOptions;if(!layout||typeof layout!=="object"||!LAYOUT_MODES.includes(layout.mode)||!LAYOUT_DENSITIES.includes(layout.density))
590
+ throw new Error("CanopyX: invalid saved layout.");if(options)result.layout={mode:options.modes.includes(layout.mode)?layout.mode:options.mode,density:layout.density}}
591
+ if(state.highlight!==undefined){if(typeof state.highlight!=="boolean")throw new Error("CanopyX: invalid saved highlight choice.");if(view.highlightOptions)result.highlight=state.highlight}
592
+ return result}
593
+ function densityRowHeight(view,density){const steps=DENSITY_STEP[view.kind];return Math.max(20,Math.min(64,view.baseRowH+steps[density]- steps[view.declaredDensity]))}
594
+ const VIEW_MODE_METHODS={prepareViewModes(){const view=this.view,columns=this.columns||view.columns;this.highlightOptions=readSearchHighlight(view.searchHighlight,columns);this.declaredDensity=this.kind==="grid"?view.density||"default":"default";this.layoutOptions=readLayout(view.layout,this.kind,this.declaredDensity);this.baseRowH=this.rowH;this.adoptModes({layout:this.layoutOptions||{mode:"fit",density:this.declaredDensity},highlight:!!this.highlightOptions?.enabled})},adoptModes(choice){if(choice.layout){this.layoutMode=choice.layout.mode;this.density=choice.layout.density}
595
+ if(choice.highlight!==undefined)this.highlightOn=choice.highlight;this.rowH=densityRowHeight(this,this.density);if(this.layoutMode==="fit")this.host.removeAttribute("data-cx-layout");else this.host.setAttribute("data-cx-layout",this.layoutMode)},modeState(state){if(this.layoutOptions)state.layout={mode:this.layoutMode,density:this.density};if(this.highlightOptions)state.highlight=!!this.highlightOn;return state},applyModeState(state){if(state.layout&&(state.layout.mode!==this.layoutMode||state.layout.density!==this.density))
596
+ this.setLayout(state.layout).catch(error=>this.emit("error",{error}));if(state.highlight!==undefined&&state.highlight!==!!this.highlightOn)
597
+ this.setSearchHighlight(state.highlight).catch(error=>this.emit("error",{error}))},startViewModes(){const parts=[this.highlightOptions&&"highlight",this.layoutOptions&&"layouts"].filter(Boolean);return Promise.all(parts.map(name=>this.requirePart(name).then(()=>this.destroyed||VIEW_MODES[name+"Start"](this))
598
+ .catch(error=>this.emit("error",{error}))))},getLayout(){return Object.freeze({mode:this.layoutMode,density:this.density,modes:this.layoutOptions?this.layoutOptions.modes:Object.freeze(["fit"])})},setLayout(change){if(!this.layoutOptions)return Promise.reject(new Error("CanopyX: this view declares no layout modes."));return this.requirePart("layouts").then(()=>VIEW_MODES.setLayout(this,change))},getMatches(){return VIEW_MODES.matches?VIEW_MODES.matches(this):Object.freeze({enabled:!!this.highlightOn,total:0,current:0,complete:true})},setSearchHighlight(enabled){if(typeof enabled!=="boolean")return Promise.reject(new Error("CanopyX: the highlight switch is a boolean."));if(!this.highlightOptions)return Promise.reject(new Error("CanopyX: this view declares no search highlighting."));return this.requirePart("highlight").then(()=>VIEW_MODES.setHighlight(this,enabled))},nextMatch(options){return this.stepMatch(1,options)},previousMatch(options){return this.stepMatch(-1,options)},stepMatch(direction,options){if(!this.highlightOptions)return Promise.reject(new Error("CanopyX: this view declares no search highlighting."));return this.requirePart("highlight").then(()=>VIEW_MODES.step(this,direction,options?.focus!==false))}};Object.assign(TreeGrid.prototype,VIEW_MODE_METHODS);const viewModeContext=()=>({VIEW_MODES,TreeGrid,h,setText,fill,numberFormat,foldText,cellText,cellValue,isCellObject,setAttr,toneOf,watchFlyout,densityRowHeight,MAX_PROTOCOL_INTEGER,LAYOUT_DENSITIES,plural,svgIcon});definePart(TreeGrid,"highlight","search highlighting",viewModeContext);definePart(TreeGrid,"layouts","layout modes",viewModeContext);const MODES_TOOLBAR=TreeGrid.prototype.buildToolbar;TreeGrid.prototype.buildToolbar=function(){MODES_TOOLBAR.call(this);if(this.highlightOptions||this.layoutOptions)this.modesReady=Promise.resolve(this.toolsReady).then(()=>this.destroyed||this.startViewModes());this.lifetime.signal.addEventListener("abort",()=>this.host.removeAttribute("data-cx-layout"),{once:true})};Object.assign(TreeGrid.prototype,{destroy(){if(this.destroyed)return;this.destroyed=true;for(const row of[...this.rendered.values(),...this.pool])disposeRowHostCells(row);this.lifetime.abort();this.dataLifetime.abort();if(this.queryController)this.queryController.abort();if(this.restoreController)this.restoreController.abort();if(this.cancelResize)this.cancelResize();clearTimeout(this.saveTimer);clearTimeout(this.layoutTimer);if(this.cancelSearch)this.cancelSearch();if(this.disposeSearchFlyout)this.disposeSearchFlyout();cancelAnimationFrame(this.rafId);this.resizeObserver.disconnect();if(this.detailObserver)this.detailObserver.disconnect();this.grid.removeEventListener("scroll",this.onScroll);this.grid.removeEventListener("wheel",this.onScaledWheel);this.grid.removeEventListener("touchstart",this.onScaledTouchStart);this.grid.removeEventListener("touchmove",this.onScaledTouchMove);this.grid.removeEventListener("touchend",this.onScaledTouchEnd);this.grid.removeEventListener("touchcancel",this.onScaledTouchEnd);global.removeEventListener("beforeprint",this.onPrint);global.removeEventListener("hashchange",this.onHash);global.removeEventListener("popstate",this.onHash);if(this.linkOpening)this.linkOpening.abort();[this.toolbar,this.grid,this.live,this.outline].forEach(element=>element.remove());this.host.classList.remove("cx-pin-tree");this.host.removeAttribute("data-cx-ready");this.host.removeAttribute("data-canopyx-mounted");this.keyMap.clear();this.selected.clear();this.rendered.clear();this.pool.length=0;if(this.inactiveCache)this.inactiveCache.clear();if(this.inlineRetained)this.inlineRetained.clear();this.rows=[];this.tops=[0];this.total=0;this.root.children=null;this.root.inlineCache=null;this.root.inlineOrder=null;this.focused=null;this.anchor=null;this.source=null;this.touchScroll=null;this.opts={};for(const column of this.columns)column.render=null;this.filter=null;this.declaredFilter=null;this.pendingRestorationToggles=null;this.scrolledCell=null;this.focusHandoff=null;this.cancelResize=null;this.cancelSearch=null;this.disposeSearchFlyout=null;this.canvas.replaceChildren();this.outline.replaceChildren()}});const ROW_HELPERS={};const GRID_REPORTING={};function gridMomentFromParts(year,month,day,hours,minutes,seconds,local){const parts=[year,month,day,hours,minutes,seconds];if(!parts.every(Number.isInteger))return null;const moment=new Date(0),zone=local?"":"UTC";moment["set"+zone+"FullYear"](year,month-1,day);moment["set"+zone+"Hours"](hours,minutes,seconds,0);const actual=["FullYear","Month","Date","Hours","Minutes","Seconds"].map(name=>moment["get"+zone+name]());actual[1]++;return parts.every((value,index)=>value===actual[index])?moment:null}
599
+ function gridDateInputMoment(text,local){const match=/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{1,3}))?)?$/.exec(text);
600
+ if(!match)return null;const moment=gridMomentFromParts(Number(match[1]),Number(match[2]),Number(match[3]),Number(match[4]),Number(match[5]),Number(match[6]||0),local);if(moment)moment.setTime(moment.getTime()+ Number((match[7]||"").padEnd(3,"0")));return moment}
601
+ function gridFreeze(value){if(value&&typeof value==="object"&&!Object.isFrozen(value)){Object.values(value).forEach(gridFreeze);Object.freeze(value)}
525
602
  return value}
526
- for(const name of["buildHeader","visibleColumns","applyWidths","setColumnWidth","startResize","cell","newCell","paintSlot","paintBadges","paintHostSlot","setSlotContent","setTimeZone","rowCells","heldCell","restoreCell","focusCell","revealInRow","revealInCell","settleCell"])
527
- ROW_HELPERS[name]=TreeGrid.prototype[name];DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridSelectionCount:"{count} selected",gridClearSelection:"Clear selection",gridSelectionQueryUnknown:"All matching records selected; {excluded} excluded",gridFitContent:"Fit to content",gridUnpin:"Unpin",gridWhere:"Where",gridComplexFilter:"This column belongs to a mixed condition group. Edit it in Filters.",gridPinLeft:"Pin left",gridPinRight:"Pin right",gridResponsiveHidden:"Hidden at this width",gridFilterMatch:"Match",gridFilterAll:"All",gridFilterAny:"Any",gridDeleteFilterGroup:"Remove condition group",gridFacetSearch:"Search values",gridFacetChoose:"Choose a value",gridFacetCounts:"{count} matching rows; {shown} of {total} values.",gridTrue:"True",gridFalse:"False",gridHeaderFilter:"Filter {column}",gridClearColumnFilter:"Clear filter",gridSelectionAggregate:"Count: {count} · Sum: {sum} · Average: {average} · Minimum: {minimum} · Maximum: {maximum}",gridConditionValue:"Value",gridConditionUpperValue:"Upper value",gridDeleteCondition:"Remove condition",gridShowRowsWhere:"Show rows where",gridOfTheseAreTrue:"of these are true",gridOfThese:"of these",gridGroupCount:"{count} groups",gridMore:"More",gridDone:"Done",gridClearAll:"Clear all",gridRows:"rows",gridResultNoun:"{count} of {total} {noun}",gridSearchNoun:"Search {noun}",gridChart:"Chart",gridPrint:"Print",gridColumnMenu:"Menu for {column}",gridSortAscending:"Sort ascending",gridSortDescending:"Sort descending",gridClearSort:"Clear sort",gridFilterColumn:"Filter…",gridHideColumn:"Hide column",gridGroupedBy:"Grouped by",gridRemoveGrouping:"Remove grouping",gridSelectAllValues:"Select all values",gridFilterValues:"Search values",gridAddCondition:"+ Condition",gridAddGroup:"+ Group",gridAnd:"and",gridOr:"or",gridEditAsText:"Edit as text",gridFilterText:"Filter expression",gridDeepFilters:"This saved filter has more than two nesting levels.",gridToday:"Today",gridYesterday:"Yesterday",gridLastSevenDays:"Last 7 days",gridLastThirtyDays:"Last 30 days",gridThisMonth:"This month",gridDateRange:"Range",gridEmpty:"No {noun}",gridEmptyReason:"There are no records to display.",gridNoMatches:"No matching {noun}",gridNoMatchesReason:"Search: {search}. Active filters: {count}.",gridRetry:"Retry",gridLoadFailed:"Could not load records",gridAll:"All",gridPageOf:"{page} of {pages}",gridStatusCount:"{count} {noun} · {selected} selected",gridDetailsSection:"Details",gridPreviousRecord:"Previous record",gridNextRecord:"Next record",gridCloseRecord:"Close record details",gridShowMore:"Show more",gridNoData:"No data",gridPeriodRange:"{from}–{to}: {state}",}));const GRID_ICONS={search:'<circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/>',filter:'<path d="M4 5h16l-6 7.5V19l-4-2v-4.5z"/>',columns:'<rect x="3.5" y="4.5" width="17" height="15" rx="2"/><path d="M9.5 4.5v15M14.5 4.5v15"/>',views:'<path d="M6 4h12v16l-6-4-6 4z"/>',export:'<path d="M12 4v11M7.5 10.5 12 15l4.5-4.5M5 19h14"/>',more:'<circle cx="5" cy="12" r="1.2"/><circle cx="12" cy="12" r="1.2"/><circle cx="19" cy="12" r="1.2"/>',close:'<path d="M6 6l12 12M18 6 6 18"/>',check:'<path d="m5 12.5 4.5 4.5L19 7.5"/>',chart:'<path d="M4 19h16M7 16V10M12 16V6M17 16v-4"/>',copy:'<rect x="8" y="8" width="11" height="11" rx="2"/><path d="M16 8V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h2"/>',up:'<path d="M12 19V5M6 11l6-6 6 6"/>',down:'<path d="M12 5v14M6 13l6 6 6-6"/>',retry:'<path d="M4 12a8 8 0 1 0 2.4-5.7M4 4v4.5h4.5"/>',previous:'<path d="m14 7-5 5 5 5"/>',next:'<path d="m10 7 5 5-5 5"/>',first:'<path d="m13 7-5 5 5 5M17 7l-5 5 5 5"/>',last:'<path d="m11 7 5 5-5 5M7 7l5 5-5 5"/>'};function gridIcon(name){const svg=document.createElementNS("http://www.w3.org/2000/svg","svg");svg.classList.add("cx-ico");svg.setAttribute("viewBox","0 0 24 24");svg.setAttribute("aria-hidden","true");svg.innerHTML=GRID_ICONS[name]||GRID_ICONS.more;return svg}
603
+ for(const name of["buildHeader","visibleColumns","applyWidths","setColumnWidth","startResize","cell","newCell","paintSlot","paintBadges","cellTip","paintHostSlot","setSlotContent","setTimeZone","rowCells","heldCell","restoreCell","focusCell","revealInRow","revealInCell","settleCell"])
604
+ ROW_HELPERS[name]=TreeGrid.prototype[name];DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridSelectionCount:"{count} selected",gridClearSelection:"Clear selection",gridSelectionQueryUnknown:"All matching records selected; {excluded} excluded",gridFitContent:"Fit to content",gridUnpin:"Unpin",gridWhere:"Where",gridComplexFilter:"This column belongs to a mixed condition group. Edit it in Filters.",gridPinLeft:"Pin left",gridPinRight:"Pin right",gridResponsiveHidden:"Hidden at this width",gridFilterMatch:"Match",gridFilterAll:"All",gridFilterAny:"Any",gridDeleteFilterGroup:"Remove condition group",gridFacetSearch:"Search values",gridFacetChoose:"Choose a value",gridFacetCounts:"{count} matching rows; {shown} of {total} values.",gridTrue:"True",gridFalse:"False",gridHeaderFilter:"Filter {column}",gridClearColumnFilter:"Clear filter",gridSelectionAggregate:"Count: {count} · Sum: {sum} · Average: {average} · Minimum: {minimum} · Maximum: {maximum}",gridConditionValue:"Value",gridConditionUpperValue:"Upper value",gridDeleteCondition:"Remove condition",gridShowRowsWhere:"Show rows where",gridOfTheseAreTrue:"of these are true",gridOfThese:"of these",gridGroupCount:"{count} groups",gridMore:"More",gridDone:"Done",gridClearAll:"Clear all",gridRows:"rows",gridResultNoun:"{count} of {total} {noun}",gridSearchNoun:"Search {noun}",gridChart:"Chart",gridPrint:"Print",gridRowPicked:"Picked up {title}. Move to a calendar and press Enter to drop it.",gridColumnMenu:"Menu for {column}",gridSortAscending:"Sort ascending",gridSortDescending:"Sort descending",gridClearSort:"Clear sort",gridFilterColumn:"Filter…",gridHideColumn:"Hide column",gridGroupedBy:"Grouped by",gridRemoveGrouping:"Remove grouping",gridSelectAllValues:"Select all values",gridFilterValues:"Search values",gridAddCondition:"+ Condition",gridAddGroup:"+ Group",gridAnd:"and",gridOr:"or",gridEditAsText:"Edit as text",gridFilterText:"Filter expression",gridDeepFilters:"This saved filter has more than two nesting levels.",gridToday:"Today",gridYesterday:"Yesterday",gridLastSevenDays:"Last 7 days",gridLastThirtyDays:"Last 30 days",gridThisMonth:"This month",gridDateRange:"Range",gridEmpty:"No {noun}",gridEmptyReason:"There are no records to display.",gridNoMatches:"No matching {noun}",gridNoMatchesReason:"Search: {search}. Active filters: {count}.",gridRetry:"Retry",gridLoadFailed:"Could not load records",gridAll:"All",gridPageOf:"{page} of {pages}",gridStatusCount:"{count} {noun} · {selected} selected",gridDetailsSection:"Details",gridPreviousRecord:"Previous record",gridNextRecord:"Next record",gridCloseRecord:"Close record details",gridShowMore:"Show more",gridNoData:"No data",gridPeriodRange:"{from}–{to}: {state}",}));const GRID_ICONS={search:'<circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/>',filter:'<path d="M4 5h16l-6 7.5V19l-4-2v-4.5z"/>',columns:'<rect x="3.5" y="4.5" width="17" height="15" rx="2"/><path d="M9.5 4.5v15M14.5 4.5v15"/>',views:'<path d="M6 4h12v16l-6-4-6 4z"/>',export:'<path d="M12 4v11M7.5 10.5 12 15l4.5-4.5M5 19h14"/>',more:'<circle cx="5" cy="12" r="1.2"/><circle cx="12" cy="12" r="1.2"/><circle cx="19" cy="12" r="1.2"/>',close:'<path d="M6 6l12 12M18 6 6 18"/>',check:'<path d="m5 12.5 4.5 4.5L19 7.5"/>',chart:'<path d="M4 19h16M7 16V10M12 16V6M17 16v-4"/>',copy:'<rect x="8" y="8" width="11" height="11" rx="2"/><path d="M16 8V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h2"/>',up:'<path d="M12 19V5M6 11l6-6 6 6"/>',down:'<path d="M12 5v14M6 13l6 6 6-6"/>',retry:'<path d="M4 12a8 8 0 1 0 2.4-5.7M4 4v4.5h4.5"/>',previous:'<path d="m14 7-5 5 5 5"/>',next:'<path d="m10 7 5 5-5 5"/>',first:'<path d="m13 7-5 5 5 5M17 7l-5 5 5 5"/>',last:'<path d="m11 7 5 5-5 5M7 7l5 5-5 5"/>'};function gridIcon(name,shapes){return svgIcon(shapes||GRID_ICONS[name]||GRID_ICONS.more)}
528
605
  function gridDecorateButton(button,icon,ghost){button.classList.add("cx-btn");if(ghost)button.classList.add("ghost");if(icon)button.prepend(gridIcon(icon));return button}
529
606
  function gridFilterCount(query){const count=group=>group?group.filters.length+group.groups.reduce((sum,child)=>sum+count(child),0):0;return(query?.filters?.length||0)+ count(query?.filterGroup)}
530
607
  const GRID_FILTER_LABELS={eq:"=",ne:"≠",contains:"gridContains",startsWith:"gridStartsWith",in:"gridIn",gt:">",ge:"≥",lt:"<",le:"≤",isNull:"gridIsNull",isNotNull:"gridIsNotNull"};function gridQueryRecord(input,record,strict,cursor){if(strict&&Object.keys(input).some(key=>!Object.hasOwn(record,key)&&!(cursor&&key==="offset")))throw new Error("CanopyX: invalid state query.");return gridFreeze(record)}
531
- function gridPredicate(filter,columns,budget){if(++budget.predicates>64)throw new Error("CanopyX: at most 64 grid predicates.");const column=filter&&columns.get(filter.column),values=filter&&filter.values;if(!column||!Object.keys(GRID_FILTER_LABELS).includes(filter.operator)||
608
+ function gridPredicate(filter,columns,budget){if(++budget.predicates>64)throw new Error("CanopyX: at most 64 grid predicates.");const column=filter&&columns.get(filter.column),values=filter&&filter.values;if(!column||budget.filterable&&column.filterable===false||!Object.keys(GRID_FILTER_LABELS).includes(filter.operator)||
532
609
  !Array.isArray(values))
533
610
  throw new Error("CanopyX: invalid grid predicate.");const nullTest=filter.operator==="isNull"||filter.operator==="isNotNull";if(nullTest?values.length!==0:filter.operator==="in"?values.length<1||values.length>1000:values.length!==1)
534
611
  throw new Error("CanopyX: invalid grid predicate arity.");if(["contains","startsWith"].includes(filter.operator)&&(column.kind!=="text"||values.some(value=>typeof value!=="string"))||
@@ -544,30 +621,19 @@ function gridSearchTerms(text,mode){if(mode!=="words")return text?[foldText(text
544
621
  }
545
622
  function gridMatchesSearch(row,terms,columns){return terms.every(term=>columns.some(column=>{const cell=row.cells[column.id],text=isCellObject(cell)&&cell.searchText!==undefined?cell.searchText:cellValue(cell);return foldText(text).includes(term)}))}
546
623
  function gridQueryStart(query,patch){return{...query,...patch,offset:0,cursor:undefined,revision:undefined}}
547
- function gridScalar(cell,column){const value=cellValue(cell);if(value==null)return null;if(column.kind==="number"){if(typeof value!=="number"||!Number.isFinite(value)||Math.abs(value)>Number.MAX_SAFE_INTEGER)
548
- throw new Error("CanopyX: numeric grid cells need finite values within the safe integer magnitude.");return value}
549
- if(column.kind==="datetime"){const date=typeof value==="string"&&/^\d{4}-\d\d-\d\dT.*(?:Z|[+-]\d\d:\d\d)$/.test(value) ? Date.parse(value) : NaN;
550
- if(!Number.isFinite(date))
551
- throw new Error("CanopyX: date grid cells need ISO dates with an explicit time zone.");return date}
552
- if(typeof value!=="string"&&typeof value!=="boolean")throw new Error("CanopyX: text grid cells need strings or booleans.");return value}
553
- function validateGridRows(rows,columns){if(!Array.isArray(rows))throw new Error("CanopyX: grid rows must be an array.");const ids=new Set(),declared=new Map(columns.map(column=>[column.id,column]));for(const row of rows){requireIdentifier(row&&row.id,"grid row ID");if(ids.has(row.id))throw new Error("CanopyX: duplicate grid row ID "+row.id);ids.add(row.id);if(!row.cells||typeof row.cells!=="object"||Array.isArray(row.cells))
554
- throw new Error("CanopyX: invalid flat row fields.");gridDecoration(row.tone,row.className);for(const id of Object.keys(row.cells)){if(!declared.has(id))throw new Error("CanopyX: undeclared grid column "+id);const cell=row.cells[id];if(isCellObject(cell)){if(Array.isArray(cell)||
555
- ["text","searchText","title","tone","secondary"].some(name=>cell[name]!==undefined&&typeof cell[name]!=="string")||
556
- cell.secondary!==undefined&&cell.secondary.length>4096||cell.series!==undefined&&!gridSeriesValid(cell.series)||
557
- cell.badges!==undefined&&(!Array.isArray(cell.badges)||cell.badges.some(badge=>!badge||typeof badge.text!=="string"||
558
- ["tone","title"].some(name=>badge[name]!==undefined&&typeof badge[name]!=="string"))))
559
- throw new Error("CanopyX: malformed grid cell.")}
560
- gridScalar(cell,declared.get(id))}}
561
- return rows}
624
+ function gridNumberFormat(format){const minimum=format?.minimumFractionDigits??0,maximum=format?.maximumFractionDigits??3;if(!format||typeof format!=="object"||Array.isArray(format)||![minimum,maximum].every(digits=>Number.isInteger(digits)&&digits>=0&&digits<=10)||minimum>maximum||
625
+ Object.keys(format).some(key=>!["minimumFractionDigits","maximumFractionDigits","prefix","suffix"].includes(key))||
626
+ [format.prefix,format.suffix].some(text=>text!==undefined&&(typeof text!=="string"||!text||text.length>16)))throw new Error("CanopyX: invalid number format.")}
562
627
  function validateGridView(view){checkDocument(view,"grid");requireIdentifier(view.id,"grid ID");if(typeof view.label!=="string"||!view.label.trim()||!Array.isArray(view.columns)||!view.columns.length)
563
628
  throw new Error("CanopyX: a grid needs a label and columns.");if(view.reporting!==undefined){if(!GRID_REPORTING.validate)throw new Error("CanopyX: this view needs the reporting runtime.");GRID_REPORTING.validate(view)}
564
- const ids=new Set();for(const column of view.columns){requireIdentifier(column&&column.id,"grid column ID");if(ids.has(column.id)||typeof column.title!=="string"||!["text","number","datetime"].includes(column.kind)||column.editable||
629
+ const ids=new Set();for(const column of view.columns){requireIdentifier(column&&column.id,"grid column ID");if(ids.has(column.id)||typeof column.title!=="string"||!["text","number","datetime"].includes(column.kind)||
565
630
  column.width!==undefined&&(!Number.isInteger(column.width)||column.width<40)||
566
- ["wrap","autoWidth","fixed","rowHeader","alwaysVisible"].some(name=>column[name]!==undefined&&typeof column[name]!=="boolean")||
631
+ ["wrap","autoWidth","fixed","rowHeader","alwaysVisible","editable","filterable","exportable"].some(name=>column[name]!==undefined&&typeof column[name]!=="boolean")||
567
632
  column.flex!==undefined&&!(typeof column.flex==="number"&&column.flex>=0&&column.flex<=100)||column.autoSize!==undefined&&!["content","header","none"].includes(column.autoSize)||
568
633
  column.pinned!==undefined&&![null,"left","right"].includes(column.pinned)||column.priority!==undefined&&!gridPriority(column.priority)||
569
- column.fixed&&column.width===undefined)
570
- throw new Error("CanopyX: invalid or duplicate grid column.");ids.add(column.id);if(column.presenter!==undefined)gridPresenter(column.presenter,column,view.columns);if(column.aggregatable!==undefined&&(typeof column.aggregatable!=="boolean"||column.aggregatable&&column.kind!=="number")||
634
+ column.fixed&&column.width===undefined||column.tooltip!==undefined&&column.tooltip!=="auto"&&!view.columns.some(item=>item.id===column.tooltip))
635
+ throw new Error("CanopyX: invalid or duplicate grid column.");ids.add(column.id);if(column.presenter!==undefined)gridPresenter(column.presenter,column,view.columns);if(column.format!==undefined){if(column.kind!=="number")throw new Error("CanopyX: number formats need number columns.");gridNumberFormat(column.format)}
636
+ if(column.aggregatable!==undefined&&(typeof column.aggregatable!=="boolean"||column.aggregatable&&column.kind!=="number")||
571
637
  column.section!==undefined&&(typeof column.section!=="string"||!column.section.trim()||column.section.length>256))
572
638
  throw new Error("CanopyX: invalid column aggregate or drawer section.")}
573
639
  if(view.columns.every(column=>column.hidden)||view.columns.filter(column=>column.rowHeader).length>1)
@@ -578,7 +644,7 @@ view.noun!==undefined&&(typeof view.noun!=="string"||!view.noun.trim()||view.nou
578
644
  view.emptyReason!==undefined&&(typeof view.emptyReason!=="string"||view.emptyReason.length>4096))throw new Error("CanopyX: invalid grid appearance or state text.");validateGridHighlights(view);if(view.searchDelayMs!==undefined&&(!Number.isInteger(view.searchDelayMs)||view.searchDelayMs<0||view.searchDelayMs>5000))
579
645
  throw new Error("CanopyX: search delay must be an integer from 0 to 5000 milliseconds.");if(view.searchColumns!==undefined&&(!Array.isArray(view.searchColumns)||!view.searchColumns.length||
580
646
  new Set(view.searchColumns).size!==view.searchColumns.length||view.searchColumns.some(id=>!ids.has(id))))
581
- throw new Error("CanopyX: search columns must identify unique declared columns.");if(view.autoRowHeight!==undefined&&typeof view.autoRowHeight!=="boolean"||
647
+ throw new Error("CanopyX: search columns must identify unique declared columns.");if(view.exportFormats!==undefined&&!exportFormatIds(view.exportFormats))throw new Error("CanopyX: exportFormats lists unique format IDs.");if(view.autoRowHeight!==undefined&&typeof view.autoRowHeight!=="boolean"||
582
648
  view.rowHeight!==undefined&&view.rowHeight!=="auto"&&(!Number.isInteger(view.rowHeight)||view.rowHeight<20||view.rowHeight>64)||
583
649
  view.estimatedRowHeight!==undefined&&(!Number.isInteger(view.estimatedRowHeight)||view.estimatedRowHeight<20||view.estimatedRowHeight>64)||
584
650
  !Number.isInteger(view.pageSize)||view.pageSize<1||view.pageSize>10000||
@@ -586,7 +652,7 @@ view.estimatedRowHeight!==undefined&&(!Number.isInteger(view.estimatedRowHeight)
586
652
  throw new Error("CanopyX: invalid grid source navigation.");if(view.source.navigation==="cursor"&&(view.source.kind!=="host"||view.presentation!=="paged"))
587
653
  throw new Error("CanopyX: cursors need a hosted paged grid.");if(view.presentation!==undefined&&!["continuous","paged"].includes(view.presentation)||
588
654
  view.pageLength!==undefined&&(!Number.isInteger(view.pageLength)||view.pageLength<1||view.pageLength>10000))
589
- throw new Error("CanopyX: invalid grid presentation or page length.");if(view.source.kind==="inline")validateGridRows(view.rows,view.columns);else if(view.rows!==undefined)throw new Error("CanopyX: host grids cannot discard inline rows.");if(view.query!==undefined)gridQuery(view,view.query);return{...view,rowHeight:view.rowHeight??(view.density==="compact"?28:view.density==="comfortable"?42:34)}}
655
+ throw new Error("CanopyX: invalid grid presentation or page length.");if(view.rowData!==undefined){if(view.source.kind!=="inline"||view.rows!==undefined||!validStructuralCount(view.rowData?.count))throw new Error("CanopyX: invalid grid rowData.")}else if(view.source.kind==="inline")validateGridRows(view.rows,view.columns);else if(view.rows!==undefined)throw new Error("CanopyX: host grids cannot discard inline rows.");if(view.query!==undefined)gridQuery(view,view.query);return{...view,rowHeight:view.rowHeight??(view.density==="compact"?28:view.density==="comfortable"?42:34)}}
590
656
  function gridQuery(view,input,strict){input=input||{};const cursor=view.source.navigation==="cursor",type=cursor?"gridCursorQuery":"gridQuery";if(typeof input!=="object"||Array.isArray(input)||
591
657
  input.protocol!==undefined&&input.protocol!==PROTOCOL||input.type!==undefined&&input.type!==type||
592
658
  input.view!==undefined&&input.view!==view.id)throw new Error("CanopyX: invalid grid query identity.");const query={protocol:PROTOCOL,type,view:view.id,limit:input.limit===undefined?cursor?view.pageLength||50:view.pageSize:input.limit,search:input.search===undefined?"":input.search,cursor:input.cursor,revision:input.revision,searchMode:input.searchMode,filterGroup:undefined};if(cursor?input.offset!==undefined&&input.offset!==0:input.cursor!==undefined)
@@ -595,7 +661,7 @@ throw new Error("CanopyX: invalid grid query window or search.");if(input.revisi
595
661
  if(input.searchMode!==undefined&&!["substring","words"].includes(input.searchMode))throw new Error("CanopyX: invalid grid search mode.");const columns=new Map(view.columns.map(column=>[column.id,column])),sorts=new Set();if(input.sort!==undefined&&!Array.isArray(input.sort)||input.filters!==undefined&&!Array.isArray(input.filters))
596
662
  throw new Error("CanopyX: grid sort and filters must be arrays.");query.sort=(input.sort||[]).map(sort=>{if(!sort||!columns.has(sort.column)||!columns.get(sort.column).sortable||sorts.has(sort.column)||
597
663
  !["asc","desc"].includes(sort.direction))
598
- throw new Error("CanopyX: invalid grid sort column or direction.");sorts.add(sort.column);return gridQueryRecord(sort,{column:sort.column,direction:sort.direction},strict)});const budget={predicates:0,groups:0,strict};query.filters=(input.filters||[]).map(filter=>gridPredicate(filter,columns,budget));if(input.filterGroup!==undefined)query.filterGroup=gridFilterGroup(input.filterGroup,columns,budget,1);return gridQueryRecord(input,query,strict,cursor)}
664
+ throw new Error("CanopyX: invalid grid sort column or direction.");sorts.add(sort.column);return gridQueryRecord(sort,{column:sort.column,direction:sort.direction},strict)});const budget={predicates:0,groups:0,strict,filterable:true};query.filters=(input.filters||[]).map(filter=>gridPredicate(filter,columns,budget));if(input.filterGroup!==undefined)query.filterGroup=gridFilterGroup(input.filterGroup,columns,budget,1);if(input[LINK_IMPL.queryOwn])query[LINK_IMPL.queryOwn]=true;return gridQueryRecord(input,query,strict,cursor)}
599
665
  function validateGridPage(page,view,query,accepted){checkDocument(page,"gridPage");requireIdentifier(page.revision,"grid revision");if(page.view!==view.id||page.offset!==query.offset||!validStructuralCount(page.total)||!validStructuralCount(page.matched)||
600
666
  page.matched>page.total||page.offset>page.matched||!Array.isArray(page.items)||
601
667
  page.items.length!==Math.min(query.limit,page.matched-page.offset))throw new Error("CanopyX: grid page does not cover its requested window.");return checkedGridPage(page,view,query,accepted)}
@@ -603,14 +669,17 @@ function checkedGridPage(page,view,query,accepted){if(query.revision!==undefined
603
669
  (page.revision!==accepted.revision||accepted.total!==undefined&&page.total!==accepted.total||
604
670
  accepted.matched!==undefined&&page.matched!==accepted.matched))throw new Error("CanopyX: grid page snapshot changed.");validateGridRows(page.items,view.columns);return{...page,items:JSON.parse(JSON.stringify(page.items))}}
605
671
  function gridOrder(left,right){return left===right?0:left<right?-1:1}
606
- function gridCompare(left,right,column,direction){const a=gridScalar(left.cells[column.id],column),b=gridScalar(right.cells[column.id],column);if(a===null||b===null)return a===b?0:a===null?1:-1;const result=gridOrder(column.kind==="text"?String(a):a,column.kind==="text"?String(b):b);return direction==="desc"?-result:result}
607
- function gridMatches(row,predicate,column){const scalar=gridScalar(row.cells[column.id],column),op=predicate.operator;if(op==="isNull")return scalar===null;if(op==="isNotNull")return scalar!==null;if(scalar===null)return false;const normalize=value=>column.kind==="text"&&typeof value==="string"?foldText(value):value;const value=normalize(scalar),values=predicate.values.map(operand=>normalize(gridScalar(operand,column))),operand=values[0];switch(op){case "eq":return value===operand;case "ne":return value!==operand;case "in":return values.includes(value);case "contains":return String(value).includes(String(operand));case "startsWith":return String(value).startsWith(String(operand));case "gt":return value>operand;case "ge":return value>=operand;case "lt":return value<operand;case "le":return value<=operand}
608
- return false}
672
+ function gridSortScalar(cell,column){return isCellObject(cell)&&cell.sort!=null?cell.sort:gridScalar(cell,column)}
673
+ function gridCompare(left,right,column,direction){return gridKeyCompare(gridSortScalar(left.cells[column.id],column),gridSortScalar(right.cells[column.id],column),column,direction)}
674
+ function gridKeyCompare(a,b,column,direction){if(a===null||b===null)return a===b?0:a===null?1:-1;const x=column.kind==="text"&&typeof a!=="number"?String(a):a,y=column.kind==="text"&&typeof b!=="number"?String(b):b;const rank=gridOrder(typeof x,typeof y);if(rank)return rank;const result=gridOrder(x,y);return direction==="desc"?-result:result}
675
+ function gridMatches(row,predicate,column){return cellMatches(row.cells[column.id],predicate,column)}
609
676
  function gridQueryRows(view,query,columns,compare=gridCompare){const terms=gridSearchTerms(query.search,query.searchMode),searchColumns=view.searchColumns?view.searchColumns.map(id=>columns.get(id)):view.columns;const rows=view.rows.filter(row=>gridMatchesSearch(row,terms,searchColumns)&&
610
- query.filters.every(predicate=>gridMatches(row,predicate,columns.get(predicate.column)))&&gridMatchesGroup(row,query.filterGroup,columns));return compare?rows.sort((a,b)=>{for(const sort of query.sort){const compared=compare(a,b,columns.get(sort.column),sort.direction);if(compared)return compared}
611
- return gridOrder(a.id,b.id)}):rows}
677
+ query.filters.every(predicate=>gridMatches(row,predicate,columns.get(predicate.column)))&&gridMatchesGroup(row,query.filterGroup,columns));if(compare!==gridCompare||!query.sort.length)return compare?rows.sort((a,b)=>{for(const sort of query.sort){const compared=compare(a,b,columns.get(sort.column),sort.direction);if(compared)return compared}
678
+ return gridOrder(a.id,b.id)}):rows;const sorts=query.sort.map(sort=>[columns.get(sort.column),sort.direction]),keys=sorts.map(([column])=>rows.map(row=>gridSortScalar(row.cells[column.id],column)));return rows.map((row,index)=>index).sort((a,b)=>{for(let s=0;s<sorts.length;s++){const compared=gridKeyCompare(keys[s][a],keys[s][b],sorts[s][0],sorts[s][1]);if(compared)return compared}
679
+ return gridOrder(rows[a].id,rows[b].id)}).map(index=>rows[index])}
612
680
  function inlineGridSource(view){const columns=new Map(view.columns.map(column=>[column.id,column]));const compare=GRID_REPORTING.compare?.(view);let lastShape=null,ordered=null;return{query(query,options){if(options.signal.aborted)throw new DOMException("Grid cancelled.","AbortError");const shape=gridResultShape(query);if(shape!==lastShape){ordered=gridQueryRows(view,query,columns,compare);lastShape=shape}
613
- return{protocol:PROTOCOL,type:"gridPage",view:view.id,revision:"inline",offset:query.offset,total:view.rows.length,matched:ordered.length,items:ordered.slice(query.offset,query.offset+query.limit)}}}}
681
+ return{protocol:PROTOCOL,type:"gridPage",view:view.id,revision:"inline",offset:query.offset,total:view.rows.length,matched:ordered.length,items:ordered.slice(query.offset,query.offset+query.limit)}},position(query,id){if(gridResultShape(query)!==lastShape){ordered=gridQueryRows(view,query,columns,compare);lastShape=gridResultShape(query)}
682
+ return ordered.findIndex(row=>row.id===id)},row(id){return view.rows.find(row=>row.id===id)||null}}}
614
683
  function gridCursor(cursor){requireIdentifier(cursor,"grid cursor");if(cursor.length>4096)throw new Error("CanopyX: cursor exceeds 4096 code units.")}
615
684
  function validateGridCursorPage(page,view,query,accepted){checkDocument(page,"gridCursorPage");requireIdentifier(page.revision,"grid revision");if(page.view!==view.id||!validStructuralCount(page.offset)||
616
685
  page.total!==null&&!validStructuralCount(page.total)||page.matched!==null&&!validStructuralCount(page.matched)||
@@ -619,26 +688,42 @@ page.total!==null&&page.matched!==null&&page.matched>page.total||
619
688
  !page.items.length&&(page.offset!==0||next)||!page.offset&&previous||
620
689
  page.matched!==null&&next!==(end<page.matched)||page.total!==null&&next&&end>=page.total||
621
690
  query.cursor===undefined&&page.offset!==0)throw new Error("CanopyX: inconsistent cursor page.");const tokens=[page.nextCursor,page.previousCursor].filter(token=>token!==undefined);tokens.forEach(gridCursor);if(new Set(tokens).size!==tokens.length||tokens.includes(query.cursor))throw new Error("CanopyX: repeated cursor.");return checkedGridPage(page,view,query,accepted)}
622
- class DataGrid{constructor(host,options){const definition=validateGridView(options.view);this.view=JSON.parse(JSON.stringify(definition));this.host=host;this.opts=options;this.id="cx" + ++instanceSeq;this.lifetime=new AbortController();this.dataLifetime=new AbortController();this.source=options.source||(this.view.source.kind==="inline"?inlineGridSource(this.view):null);if(!this.source||typeof this.source.query!=="function")throw new Error("CanopyX: host grids need a query source.");this.cursor=this.view.source.navigation==="cursor";if(this.source.navigation!==undefined&&this.source.navigation!==(this.cursor?"cursor":"offset"))
691
+ function gridFacetKey(value,column){if(value!==null&&typeof value==="object"||value===undefined)throw new Error("CanopyX: facet values are raw scalars.");const scalar=gridScalar(value,column);if(scalar===null)return "null";return column.kind==="datetime"?"d:"+scalar:typeof scalar==="string"?"s:"+foldText(scalar):
692
+ typeof scalar==="boolean"?"b:"+scalar:"n:"+scalar}
693
+ function validateGridFacetPage(input,view,request,snapshot){checkDocument(input,"gridFacetPage");const column=view.columns.find(item=>item.id===request.column);if(input.view!==view.id||input.revision!==request.query.revision||input.column!==request.column||
694
+ input.search!==request.search||input.includeCurrentFilter!==request.includeCurrentFilter||
695
+ !Number.isInteger(input.matched)||input.matched<0||input.matched>MAX_PROTOCOL_INTEGER||
696
+ !Number.isInteger(input.totalValues)||input.totalValues<0||input.totalValues>input.matched||
697
+ !Array.isArray(input.items)||input.items.length>request.limit||input.items.length>input.totalValues||
698
+ !input.items.length&&input.totalValues>0||input.truncated!==(input.items.length<input.totalValues))
699
+ throw new Error("CanopyX: facet response must match its accepted revision, projection and bounded counts.");const widened=!request.includeCurrentFilter&&
700
+ gridResultShape(gridColumnProjection(request.query,request.column))!==gridResultShape(request.query);if(Number.isInteger(snapshot.total)&&input.matched>snapshot.total||Number.isInteger(snapshot.matched)&&
701
+ (widened?input.matched<snapshot.matched:input.matched!==snapshot.matched))
702
+ throw new Error("CanopyX: facet counts contradict the known accepted dataset counts.");const keys=new Set();let occurrences=0;const items=input.items.map(item=>{if(!item||!Object.hasOwn(item,"value")||!Number.isInteger(item.count)||item.count<1||item.count>input.matched)
703
+ throw new Error("CanopyX: invalid facet value/count bucket.");const key=gridFacetKey(item.value,column);if(keys.has(key))throw new Error("CanopyX: duplicate typed facet bucket.");keys.add(key);occurrences +=item.count;return{value:item.value,count:item.count}});if(occurrences>input.matched||!request.search&&!input.truncated&&occurrences!==input.matched)
704
+ throw new Error("CanopyX: facet buckets contradict the matching row count.");return gridFreeze({protocol:PROTOCOL,type:"gridFacetPage",view:input.view,revision:input.revision,column:input.column,search:input.search,includeCurrentFilter:input.includeCurrentFilter,matched:input.matched,totalValues:input.totalValues,truncated:input.truncated,items})}
705
+ class DataGrid{constructor(host,options){const{rows,rowData,...definition}=validateGridView(options.view),owned=parsedViews.has(options.view);this.view=JSON.parse(JSON.stringify(definition));if(rows)this.view.rows=owned?rows:JSON.parse(JSON.stringify(rows));if(rowData){this.view.rowData=owned?rowData:structuredClone(rowData);this.view.rows=[]}
706
+ this.host=host;this.opts=options;this.id="cx" + ++instanceSeq;this.lifetime=new AbortController();this.dataLifetime=new AbortController();this.source=options.source||(this.view.source.kind==="inline"?this.view.rowData?rowDataSource(this):inlineGridSource(this.view):null);if(!this.source||typeof this.source.query!=="function")throw new Error("CanopyX: host grids need a query source.");this.cursor=this.view.source.navigation==="cursor";if(this.source.navigation!==undefined&&this.source.navigation!==(this.cursor?"cursor":"offset"))
623
707
  throw new Error("CanopyX: source navigation must match the grid definition.");this.pageSize=this.view.pageSize;this.autoRows=this.view.rowHeight==="auto"||!!this.view.autoRowHeight;this.rowH=this.view.rowHeight==="auto"?this.view.estimatedRowHeight||28:this.view.rowHeight;if(this.autoRows)this.rowHeights=new Map();this.paged=this.view.presentation==="paged";this.pageLength=this.view.pageLength||50;this.pageOffset=0;this.searchDelayMs=options.searchDelayMs===undefined?this.view.searchDelayMs===undefined?
624
708
  this.view.source.kind==="inline"?0:300:this.view.searchDelayMs:options.searchDelayMs;if(!Number.isInteger(this.searchDelayMs)||this.searchDelayMs<0||this.searchDelayMs>5000)
625
- throw new Error("CanopyX: search delay must be an integer from 0 to 5000 milliseconds.");this.cachePageLimit=options.cachePageLimit===undefined?40:options.cachePageLimit;this.requestTimeoutMs=options.requestTimeoutMs===undefined?30000:options.requestTimeoutMs;if(!Number.isInteger(this.cachePageLimit)||this.cachePageLimit<2||this.cachePageLimit>1000||
709
+ throw new Error("CanopyX: search delay must be an integer from 0 to 5000 milliseconds.");this.cachePageLimit=options.cachePageLimit===undefined?40:options.cachePageLimit;this.requestTimeoutMs=options.requestTimeoutMs===undefined?rowData&&!options.source?2147483647:30000:options.requestTimeoutMs;if(!Number.isInteger(this.cachePageLimit)||this.cachePageLimit<2||this.cachePageLimit>1000||
626
710
  !Number.isInteger(this.requestTimeoutMs)||this.requestTimeoutMs<1||this.requestTimeoutMs>2147483647)
627
- throw new Error("CanopyX: invalid grid cache or request timeout.");const renderers=hostRenderers(options.renderers||{},this.view.columns);this.columns=this.view.columns.map(column=>({...column,autoWidth:column.autoWidth||column.width===undefined,render:Object.hasOwn(renderers,column.id)?renderers[column.id]:null}));this.lang=document.documentElement.lang||undefined;this.tools=Object.assign({},this.view.toolbar,options.toolbar);if(Object.entries(this.tools).some(([name,value])=>!/^(search|filters|columns|views|export|more|count)$/.test(name) || typeof value !== "boolean"))
711
+ throw new Error("CanopyX: invalid grid cache or request timeout.");this.kind="grid";this.plugins=viewPlugins(options,this.kind);const renderers=hostRenderers(options.renderers||{},this.view.columns,this.plugins);this.columns=this.view.columns.map(column=>({...column,autoWidth:column.autoWidth||column.width===undefined,render:Object.hasOwn(renderers,column.id)?renderers[column.id]:null}));this.lang=document.documentElement.lang||undefined;this.tools=Object.assign({},this.view.toolbar,options.toolbar);if(Object.entries(this.tools).some(([name,value])=>!/^(search|filters|columns|views|export|more|count|columnMenus|matches|layout)$/.test(name) || typeof value !== "boolean"))
628
712
  throw new Error("CanopyX: toolbar options are booleans by control name.");this.labels=labelsFor(this.view.labels,options.labels);this.local=this.view.timeZone==="local";this.fmt=formatting(this.lang,this.local,this.labels);this.multi=this.view.selection==="multiple";this.queryAware=true;this.queryEpoch=0;this.version=1;this.total=0;this.pages=new Map();this.rowCache=new Map();this.placeholders=new Map();this.pendingPages=new Map();this.rendered=new Map();this.pool=[];this.selected=new Set();this.focused=null;this.rafId=0;this.prepareState();this.build();this.ready=this.initializeState()}
629
- emit(name,detail){this.host.dispatchEvent(new CustomEvent("canopyx:"+name,{detail}));const callback=this.opts["on"+name[0].toUpperCase()+ name.slice(1)];if(typeof callback==="function"){try{callback(detail)}catch(error){if(global.console)global.console.error(error)}}}
713
+ emit(name,detail){let go=this.host.dispatchEvent(new CustomEvent("canopyx:"+name,{detail,cancelable:true}));const callback=this.opts["on"+name[0].toUpperCase()+ name.slice(1)];if(typeof callback==="function"){try{go=callback(detail)!==false&&go}catch(error){if(global.console)global.console.error(error)}}
714
+ return go}
630
715
  stateChanged(){if(this.destroyed||this.restoringEpoch===this.queryEpoch)return;clearTimeout(this.stateTimer);this.stateTimer=setTimeout(()=>{if(this.destroyed)return;const state=this.getState();this.writeStoredState(state);this.emit("state",{query:this.getQuery(),state})},200)}
631
716
  getQuery(){return this.acceptedQuery||null}
632
717
  queryForChange(includeQueuedInput){const query=this.pendingQuery||this.acceptedQuery||{};return includeQueuedInput&&this.searchTimer!=null&&!this.searchComposing?
633
718
  gridQueryStart(query,{search:this.searchInput.value}):query}
634
719
  async setQuery(input,paging){if(this.destroyed)throw new DOMException("Grid was destroyed.","AbortError");const length=paging?paging.pageLength:this.pageLength;const initial=gridQuery(this.view,this.cursor?{...input,limit:length}:input);const desired=this.cursor?0:Math.floor(initial.offset / length) * length;
635
720
  const same=this.acceptedQuery&&gridResultShape(initial)===gridResultShape(this.acceptedQuery)&&initial.revision===this.acceptedQuery.revision;let query=this.paged&&!this.cursor?gridQuery(this.view,{...initial,offset:same?Math.min(desired,Math.max(0,Math.ceil(this.rowCount()/ length) - 1) * length) : 0 }) : initial;
636
- const epoch=++this.queryEpoch;clearTimeout(this.searchTimer);this.searchTimer=null;this.dataLifetime.abort();this.dataLifetime=new AbortController();const lifetime=this.dataLifetime;this.pendingPages.clear();this.pendingQuery=query;this.setNotice(this.labels.loading,false);this.grid.setAttribute("aria-busy","true");this.syncPager();try{let page=(this.cursor?validateGridCursorPage:validateGridPage)(await this.requestPage(query,lifetime.signal),this.view,query,same?this.snapshot:null);if(this.cursor&&paging&&paging.direction)this.validateCursorTransition(page,paging);if(this.paged&&!this.cursor&&!same&&desired){gridOperationActive(this,lifetime.signal);const offset=Math.min(desired,Math.max(0,Math.ceil(page.matched / length) - 1) * length);
721
+ const epoch=++this.queryEpoch;clearTimeout(this.searchTimer);this.searchTimer=null;this.dataLifetime.abort();this.dataLifetime=new AbortController();const lifetime=this.dataLifetime;this.pendingPages.clear();this.pendingQuery=query;this.pendingPaging=paging?{...paging,pageLength:length}:null;this.setNotice(this.labels.loading,false);this.grid.setAttribute("aria-busy","true");this.syncPager();try{let page=(this.cursor?validateGridCursorPage:validateGridPage)(await this.requestPage(query,lifetime.signal),this.view,query,same?this.snapshot:null);if(this.cursor&&paging&&paging.direction)this.validateCursorTransition(page,paging);if(this.paged&&!this.cursor&&!same&&desired){gridOperationActive(this,lifetime.signal);const offset=Math.min(desired,Math.max(0,Math.ceil(page.matched / length) - 1) * length);
637
722
  if(offset){const snapshot={revision:page.revision,total:page.total,matched:page.matched};query=gridQuery(this.view,{...query,offset,revision:page.revision});page=validateGridPage(await this.requestPage(query,lifetime.signal),this.view,query,snapshot)}}
638
723
  gridOperationActive(this,lifetime.signal);const hadFocus=this.canvas.contains(document.activeElement),previousId=this.focused&&this.focused.key;this.pages.clear();this.rowCache.clear();this.placeholders.clear();this.focused=null;this.acceptedQuery=gridQuery(this.view,{...query,revision:page.revision});this.snapshot={revision:page.revision,total:page.total,matched:page.matched};this.pageLength=length;if(paging)this.pageAll=!!paging.allRows;this.pageOffset=this.cursor?page.offset:this.paged?query.offset:0;if(this.cursor)this.cursorWindow={end:page.offset+page.items.length,next:page.nextCursor,previous:page.previousCursor};if(this.autoRows){if(!same)this.rowHeights.clear();this.rowOffsets=null}
639
- this.storePage(page);this.pendingQuery=null;this.lastFailedQuery=null;this.lastFailedPaging=null;this.version++;const hasRows=this.cursor?page.items.length:page.matched;this.total=(this.cursor?page.items.length:this.paged?Math.min(length,page.matched-this.pageOffset):page.matched)*this.rowH;this.grid.setAttribute("aria-rowcount",page.matched===null?"-1":String(page.matched+1));this.grid.tabIndex=hasRows?-1:0;this.syncScrollRange(this.paged?0:query.offset*this.rowH);this.buildHeader();this.syncColumnControls();this.setNotice(hasRows?"":this.labels.searchNoResults,false);this.grid.setAttribute("aria-busy","false");if(hadFocus){this.focused=[...this.rowCache.values()].find(row=>row.key===previousId)||this.rowCache.get(this.cursor?page.offset:query.offset)||null}
724
+ this.storePage(page);this.pendingQuery=null;this.pendingPaging=null;this.lastFailedQuery=null;this.lastFailedPaging=null;this.version++;const hasRows=this.cursor?page.items.length:page.matched;this.total=(this.cursor?page.items.length:this.paged?Math.min(length,page.matched-this.pageOffset):page.matched)*this.rowH;this.grid.setAttribute("aria-rowcount",page.matched===null?"-1":String(page.matched+1 +(this.headDepth||0)));this.grid.tabIndex=hasRows?-1:0;this.syncScrollRange(this.paged?0:query.offset*this.rowH);this.buildHeader();this.syncColumnControls();this.setNotice(hasRows?"":this.labels.searchNoResults,false);this.grid.setAttribute("aria-busy","false");if(hadFocus){this.focused=[...this.rowCache.values()].find(row=>row.key===previousId)||this.rowCache.get(this.cursor?page.offset:query.offset)||null}
640
725
  this.render();if(hadFocus){const element=this.focused&&this.rendered.get(this.focused);(element||this.grid).focus({preventScroll:true})}
641
- this.host.setAttribute("data-cx-ready","true");if(this.searchInput&&!this.searchComposing&&this.searchTimer==null)this.searchInput.value=query.search;this.syncQuerySummary();this.syncPager();this.emit("query",{query:this.acceptedQuery,revision:page.revision,total:page.total,matched:page.matched});this.stateChanged();return this.getQuery()}catch(error){if(epoch===this.queryEpoch&&!this.destroyed){this.pendingQuery=null;this.lastFailedQuery=initial;this.lastFailedPaging={...paging,pageLength:length};this.dataLifetime.abort();this.dataLifetime=new AbortController();this.grid.setAttribute("aria-busy","false");this.setNotice(fill(this.labels.queryFailed,{error:error.message}),true);this.syncPager();this.emit("error",{error})}
726
+ this.host.setAttribute("data-cx-ready","true");if(this.searchInput&&!this.searchComposing&&this.searchTimer==null)this.searchInput.value=query.search;this.syncQuerySummary();this.syncPager();this.emit("query",{query:this.acceptedQuery,revision:page.revision,total:page.total,matched:page.matched});this.stateChanged();return this.getQuery()}catch(error){if(epoch===this.queryEpoch&&!this.destroyed){this.pendingQuery=null;this.pendingPaging=null;this.lastFailedQuery=initial;this.lastFailedPaging={...paging,pageLength:length};this.dataLifetime.abort();this.dataLifetime=new AbortController();this.grid.setAttribute("aria-busy","false");this.setNotice(fill(this.labels.queryFailed,{error:error.message}),true);this.syncPager();this.emit("error",{error})}
642
727
  throw error}}
643
728
  querySortHeader(id,event){const previous=gridQuery(this.view,this.queryForChange(true)),sorts=previous.sort.slice();const found=sorts.findIndex(sort=>sort.column===id),old=found>=0?sorts[found].direction:null;const next=old==="asc"?"desc":old==="desc"?null:"asc";const changed=event&&event.shiftKey?sorts.filter(sort=>sort.column!==id):[];if(next)changed.splice(event&&event.shiftKey&&found>=0?Math.min(found,changed.length):changed.length,0,{column:id,direction:next});this.setQuery(gridQueryStart(previous,{sort:changed})).catch(()=>{})}
644
729
  sort(sorts){return this.setQuery(gridQueryStart(this.queryForChange(true),{sort:sorts}))}
@@ -656,7 +741,7 @@ observeDetail(){}
656
741
  trimInactiveCache(){this.trimPages()}
657
742
  setSelection(ids){if(this.destroyed)return;if(!Array.isArray(ids))throw new Error("CanopyX: selection must be an array of row IDs.");ids.forEach(id=>requireIdentifier(id,"selection ID"));const next=new Set(ids);if(this.view.selection==="none"&&next.size||!this.multi&&next.size>1)throw new Error("CanopyX: selection exceeds the view's mode.");this.selected=next;this.version++;this.scheduleRender();this.emit("select",{ids:[...next]});this.syncGridChrome();this.stateChanged()}
658
743
  setColumnVisible(id,visible){const column=this.columns.find(item=>item.id===id);if(!column||typeof visible!=="boolean")throw new Error("CanopyX: visibility needs a declared column and boolean.");if(!visible&&this.columns.filter(item=>!item.hidden).length===1&&!column.hidden)throw new Error("CanopyX: keep at least one column visible.");column.hidden=!visible;this.buildHeader();this.scheduleRender();this.stateChanged();this.syncColumnControls()}
659
- destroy(){if(this.destroyed)return;this.writeStoredState(this.getState());this.destroyed=true;this.queryEpoch++;this.lifetime.abort();this.dataLifetime.abort();for(const row of[...this.rendered.values(),...this.pool])disposeRowHostCells(row);if(this.rafId)cancelAnimationFrame(this.rafId);clearTimeout(this.searchTimer);this.searchTimer=null;clearTimeout(this.stateTimer);if(this.disposeControls)this.disposeControls();if(this.cancelResize)this.cancelResize();this.resizeObserver.disconnect();this.detailObserver?.disconnect();this.rowHeights?.clear();this.rowOffsets=null;this.restoreHeight();this.host.classList.remove("cx-data-grid","cx-phone","cx-has-selection","cx-scroll-left","cx-scroll-right","cx-grid-awaiting","cx-pins");this.grid.removeEventListener("wheel",this.onScaledWheel);this.grid.removeEventListener("touchstart",this.onScaledTouchStart);this.grid.removeEventListener("touchmove",this.onScaledTouchMove);this.grid.removeEventListener("touchend",this.onScaledTouchEnd);this.grid.removeEventListener("touchcancel",this.onScaledTouchEnd);this.pages.clear();this.rowCache.clear();this.placeholders.clear();this.pendingPages.clear();this.rendered.clear();this.pool.length=0;this.toolbar.remove();this.grid.remove();this.notice.remove();this.live.remove();this.querySummary?.remove();if(this.pager)this.pager.remove();this.statusBar?.remove();this.groupBar?.remove();this.statusAggregateController?.abort();this.host.removeAttribute("data-cx-ready");this.host.removeAttribute("data-canopyx-mounted")}}
744
+ destroy(){if(this.destroyed)return;this.writeStoredState(this.getState());this.destroyed=true;this.queryEpoch++;this.lifetime.abort();this.dataLifetime.abort();for(const row of[...this.rendered.values(),...this.pool])disposeRowHostCells(row);if(this.rafId)cancelAnimationFrame(this.rafId);clearTimeout(this.searchTimer);this.searchTimer=null;clearTimeout(this.stateTimer);if(this.disposeControls)this.disposeControls();if(this.cancelResize)this.cancelResize();this.resizeObserver.disconnect();this.detailObserver?.disconnect();this.rowHeights?.clear();this.rowOffsets=null;this.restoreHeight();this.host.classList.remove("cx-data-grid","cx-chrome","cx-phone","cx-has-selection","cx-scroll-left","cx-scroll-right","cx-grid-awaiting","cx-pins");this.grid.removeEventListener("wheel",this.onScaledWheel);this.grid.removeEventListener("touchstart",this.onScaledTouchStart);this.grid.removeEventListener("touchmove",this.onScaledTouchMove);this.grid.removeEventListener("touchend",this.onScaledTouchEnd);this.grid.removeEventListener("touchcancel",this.onScaledTouchEnd);this.pages.clear();this.rowCache.clear();this.placeholders.clear();this.pendingPages.clear();this.rendered.clear();this.pool.length=0;this.toolbar.remove();this.grid.remove();this.notice.remove();this.live.remove();this.querySummary?.remove();if(this.pager)this.pager.remove();this.statusBar?.remove();this.groupBar?.remove();this.statusAggregateController?.abort();this.host.removeAttribute("data-cx-ready");this.host.removeAttribute("data-canopyx-mounted")}}
660
745
  Object.assign(DataGrid.prototype,ROW_SCROLLING,ROW_DOM_ORDER,ROW_VIEWPORT,ROW_HELPERS);Object.assign(DataGrid.prototype,{requestPage(query,signal){return this.runGridHook(signal=>this.source.query(query,{signal}),signal)},storePage(page){const rows=page.items.map((item,i)=>({key:item.id,h:item,_ri:page.offset+i,hpx:this.rowH}));const incoming=new Map(rows.map(row=>[row.key,row._ri]));for(const cached of this.rowCache.values())if(incoming.has(cached.key)&&incoming.get(cached.key)!==cached._ri)
661
746
  throw new Error("CanopyX: inconsistent grid row identity.");for(const row of rows){const existing=this.rowCache.get(row._ri);if(existing&&existing.key!==row.key)throw new Error("CanopyX: inconsistent grid row identity.")}
662
747
  const retained=rows.map(row=>this.rowCache.get(row._ri)||row);let restoreFocus=false;for(const row of retained){const placeholder=this.placeholders.get(row._ri);if(this.focused===placeholder){const element=this.rendered.get(placeholder);restoreFocus=!!(element&&element.contains(document.activeElement));this.focused=row}
@@ -669,20 +754,20 @@ if(!this.rowCache.has(index))offsets.add(Math.floor(index / this.pageSize) * thi
669
754
  if(this.focused?.status&&this.focused._ri>=0)offsets.add(Math.floor(this.focused._ri / this.pageSize) * this.pageSize);
670
755
  for(const offset of offsets){if(this.pendingPages.size>=4)break;if(!this.pendingPages.has(offset)&&!this.pages.get(offset)?.error)this.loadPage(offset)}},loadPage(offset){if(this.pendingPages.has(offset))return this.pendingPages.get(offset).promise;const{queryEpoch:epoch,dataLifetime:lifetime,snapshot}=this;const query=gridQuery(this.view,{...this.acceptedQuery,offset,limit:this.pageSize,revision:snapshot.revision});const token={};this.pendingPages.set(offset,token);token.promise=this.requestPage(query,lifetime.signal).then(page=>{if(this.destroyed||epoch!==this.queryEpoch||lifetime!==this.dataLifetime)return;const restoreFocus=this.storePage(validateGridPage(page,this.view,query,snapshot));if(restoreFocus){this.render();const active=document.activeElement,element=this.rendered.get(this.focused);if(element&&(!active||active===document.body))element.focus({preventScroll:true})}else this.scheduleRender()}).catch(error=>{if(this.destroyed||epoch!==this.queryEpoch||lifetime!==this.dataLifetime||lifetime.signal.aborted)return;this.pages.set(offset,{error,offset});this.version++;this.setNotice(fill(this.labels.loadError,{error:error.message}),true);this.emit("error",{error});this.scheduleRender()}).finally(()=>{if(this.pendingPages.get(offset)===token){this.pendingPages.delete(offset);this.loadWindow()}});return token.promise},async waitForRow(index,epoch){while(!this.destroyed&&epoch===this.queryEpoch){const row=this.rowItem(index);if(!row||row.status!=="loading")return row;const offset=Math.floor(index / this.pageSize) * this.pageSize, pending = this.pendingPages.get(offset);
671
756
  if(pending)await pending.promise;else if(this.pendingPages.size<4)await this.loadPage(offset);else await Promise.race([...this.pendingPages.values()].map(token=>token.promise))}
672
- throw new DOMException("Grid navigation was superseded.","AbortError")},retry(){if(this.lastFailedQuery)return this.setQuery(this.lastFailedQuery,this.lastFailedPaging);for(const[offset,page]of this.pages)if(page.error)this.pages.delete(offset);this.version++;this.setNotice("",false);this.scheduleRender();return Promise.resolve()}});DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridPaging:"Result pages",gridPageRange:"{first}–{last} of {count} rows",gridPageNumber:"Page",gridPageLength:"Rows per page",gridFirstPage:"First page",gridPreviousPage:"Previous page",gridNextPage:"Next page",gridLastPage:"Last page",gridUnknownCount:"Result count unavailable",gridMatchedCount:"{count} matching rows",gridCursorRange:"Rows {first}–{last}"}));function gridResultShape(query){return JSON.stringify([query.search,query.searchMode,query.filters,query.filterGroup,query.sort])}
757
+ throw new DOMException("Grid navigation was superseded.","AbortError")},retry(){if(this.lastFailedQuery)return this.setQuery(this.lastFailedQuery,this.lastFailedPaging);for(const[offset,page]of this.pages)if(page.error)this.pages.delete(offset);this.version++;this.setNotice("",false);this.scheduleRender();return Promise.resolve()}});DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridPaging:"Result pages",gridPageRange:"{first}–{last} of {count} rows",gridPageNumber:"Page",gridPageLength:"Rows per page",gridFirstPage:"First page",gridPreviousPage:"Previous page",gridNextPage:"Next page",gridLastPage:"Last page",gridUnknownCount:"Result count unavailable",gridMatchedCount:"{count} matching rows",gridCursorRange:"Rows {first}–{last}",gridPageInfo:"Showing {first} to {last} of {count} rows",gridPageInfoFiltered:"Showing {first} to {last} of {count} rows (filtered from {total})",gridPageButton:"Page {page}"}));function gridResultShape(query){return JSON.stringify([query.search,query.searchMode,query.filters,query.filterGroup,query.sort])}
673
758
  Object.assign(DataGrid.prototype,{getPaging(){const window=this.cursorWindow,count=this.cursor?window?window.end-this.pageOffset:0:this.rowCount();return Object.freeze({presentation:this.paged?"paged":"continuous",pageLength:this.pageLength,first:count?this.pageOffset+1:0,last:this.cursor?count?window.end:0:
674
759
  this.paged?Math.min(count,this.pageOffset+this.pageLength):count,matched:this.snapshot?this.snapshot.matched:this.cursor?null:0,total:this.snapshot?this.snapshot.total:this.cursor?null:0,...this.cursor?{navigation:"cursor",pageIndex:null,pageCount:null,hasNext:!!(window&&window.next!==undefined),hasPrevious:!!(window&&window.previous!==undefined)}:{pageIndex:this.paged?Math.floor(this.pageOffset / this.pageLength) : 0,
675
760
  pageCount:this.paged?Math.ceil(count / this.pageLength) : count ? 1 : 0 } });
676
761
  },goToPage(index){if(this.cursor||!this.paged||!this.acceptedQuery||!Number.isInteger(index)||index<0)
677
- throw new Error("CanopyX: choose a nonnegative page in an accepted paged result.");const query=this.queryForChange(true),queuedSearch=query.search!==this.acceptedQuery.search;const page=queuedSearch?0:Math.min(index,Math.max(0,this.getPaging().pageCount-1));const request=gridQuery(this.view,Object.assign({},query,{offset:page*this.pageLength,limit:this.pageSize,revision:queuedSearch?undefined:query.revision}));return this.navigatePageQuery(request)},navigatePageQuery(request,paging){const key=JSON.stringify(request);const pending=this.pendingPageNavigation;if(pending&&pending.epoch===this.queryEpoch&&pending.key===key)return pending.promise;const operation=this.setQuery(request,paging),navigation={epoch:this.queryEpoch,key};navigation.promise=operation.finally(()=>{if(this.pendingPageNavigation===navigation)this.pendingPageNavigation=null});this.pendingPageNavigation=navigation;return navigation.promise},setPageLength(length){const all=length==="all";if(all){if(this.view.source.kind!=="inline")throw new Error("CanopyX: All rows needs inline data.");length=Math.max(1,this.view.rows.length)}
678
- if(!this.paged||!this.acceptedQuery||!Number.isInteger(length)||length<1||length>Math.max(10000,this.view.source.kind==="inline"?this.view.rows.length:0))
762
+ throw new Error("CanopyX: choose a nonnegative page in an accepted paged result.");const query=this.queryForChange(true),queuedSearch=query.search!==this.acceptedQuery.search;const page=queuedSearch?0:Math.min(index,Math.max(0,this.getPaging().pageCount-1));const request=gridQuery(this.view,Object.assign({},query,{offset:page*this.pageLength,limit:this.pageSize,revision:queuedSearch?undefined:query.revision}));return this.navigatePageQuery(request)},navigatePageQuery(request,paging){const key=JSON.stringify(request);const pending=this.pendingPageNavigation;if(pending&&pending.epoch===this.queryEpoch&&pending.key===key)return pending.promise;const operation=this.setQuery(request,paging),navigation={epoch:this.queryEpoch,key};navigation.promise=operation.finally(()=>{if(this.pendingPageNavigation===navigation)this.pendingPageNavigation=null});this.pendingPageNavigation=navigation;return navigation.promise},setPageLength(length){const all=length==="all";if(all){if(this.view.source.kind!=="inline")throw new Error("CanopyX: All rows needs inline data.");length=Math.max(1,(this.view.rowData?.count??this.view.rows.length))}
763
+ if(!this.paged||!this.acceptedQuery||!Number.isInteger(length)||length<1||length>Math.max(10000,this.view.source.kind==="inline"?(this.view.rowData?.count??this.view.rows.length):0))
679
764
  throw new Error("CanopyX: visible page length is an integer from 1 to 10000 for an accepted paged result.");const query=this.queryForChange(true),queuedSearch=query.search!==this.acceptedQuery.search;if(this.cursor)return this.setQuery(gridQueryStart(query),{pageLength:length,allRows:all});return this.setQuery(Object.assign({},query,{offset:queuedSearch?0:Math.floor(this.pageOffset / length) * length,
680
765
  limit:this.pageSize,revision:queuedSearch?undefined:query.revision}),{pageLength:length,allRows:all})},validateCursorTransition(page,paging){const end=page.offset+page.items.length;if(paging.direction==="first"&&page.offset!==0||paging.direction==="next"&&page.offset!==paging.edge||
681
766
  paging.direction==="previous"&&end!==paging.edge)
682
767
  throw new Error("CanopyX: cursor pages must be contiguous.");if(paging.direction!=="first"){const identities=new Set([...this.rowCache.values()].map(row=>row.key));if(page.items.some(row=>identities.has(row.id)))throw new Error("CanopyX: cursor pages repeat a row ID.")}},navigateCursor(direction){if(!this.acceptedQuery||!this.cursorWindow||!this.cursor)
683
768
  throw new Error("CanopyX: no accepted cursor page.");const query=this.queryForChange(true),changed=gridResultShape(query)!==gridResultShape(this.acceptedQuery);let cursor=direction==="next"?this.cursorWindow.next:direction==="previous"?this.cursorWindow.previous:undefined;if(changed){direction="first";cursor=undefined}
684
769
  if(direction!=="first"&&cursor===undefined){if(!this.pendingQuery)return Promise.resolve(this.acceptedQuery);direction="first"}
685
- const request=gridQuery(this.view,{...query,cursor,limit:this.pageLength,revision:changed?undefined:this.snapshot.revision});const paging={pageLength:this.pageLength,direction,edge:direction==="next"?this.cursorWindow.end:this.pageOffset};return this.navigatePageQuery(request,paging)},firstPage(){return this.cursor?this.navigateCursor("first"):this.goToPage(0)},previousPage(){return this.cursor?this.navigateCursor("previous"):this.goToPage(Math.max(0,this.getPaging().pageIndex-1))},nextPage(){return this.cursor?this.navigateCursor("next"):this.goToPage(this.getPaging().pageIndex+1)},buildPager(){if(!this.paged)return;const pager=this.pager=h("form","cx-grid-pager"),signal=this.lifetime.signal;pager.hidden=this.opts.toolbar===false;pager.setAttribute("aria-label",this.labels.gridPaging);this.pageRange=h("span","cx-grid-page-range");this.pageRange.setAttribute("role","status");const run=action=>{try{Promise.resolve(action()).catch(()=>{})}catch(error){this.emit("error",{error})}};this.pageButtons=[gridButton(this.labels.gridFirstPage,()=>run(()=>this.firstPage())),gridButton(this.labels.gridPreviousPage,()=>run(()=>this.previousPage())),gridButton(this.labels.gridNextPage,()=>run(()=>this.nextPage()))];if(!this.cursor)this.pageButtons.push(gridButton(this.labels.gridLastPage,()=>run(()=>this.goToPage(Math.max(0,this.getPaging().pageCount-1)))));const navigation=h("span","cx-pager");navigation.append(this.pageButtons[0],this.pageButtons[1]);this.pageButtons.forEach((button,index)=>{const label=button.textContent;button.replaceChildren(gridIcon(["first","previous","next","last"][index]));button.setAttribute("aria-label",label);button.title=label});if(!this.cursor){const number=this.pageNumber=h("input");number.type="number";number.min="1";number.step="1";number.required=true;number.setAttribute("aria-label",this.labels.gridPageNumber);const field=gridInputLabel(this.labels.gridPageNumber,number);field.classList.add("pg");this.pageOf=h("span");field.appendChild(this.pageOf);navigation.appendChild(field)}
770
+ const request=gridQuery(this.view,{...query,cursor,limit:this.pageLength,revision:changed?undefined:this.snapshot.revision});const paging={pageLength:this.pageLength,direction,edge:direction==="next"?this.cursorWindow.end:this.pageOffset};return this.navigatePageQuery(request,paging)},firstPage(){return this.cursor?this.navigateCursor("first"):this.goToPage(0)},previousPage(){return this.cursor?this.navigateCursor("previous"):this.goToPage(Math.max(0,this.getPaging().pageIndex-1))},nextPage(){return this.cursor?this.navigateCursor("next"):this.goToPage(this.getPaging().pageIndex+1)},buildPager(){if(!this.paged)return;const pager=this.pager=h("form","cx-grid-pager"),signal=this.lifetime.signal;pager.hidden=this.opts.toolbar===false||!!this.view.pager;pager.setAttribute("aria-label",this.labels.gridPaging);this.pageRange=h("span","cx-grid-page-range");this.pageRange.setAttribute("role","status");const run=action=>{try{Promise.resolve(action()).catch(()=>{})}catch(error){this.emit("error",{error})}};this.pageButtons=[gridButton(this.labels.gridFirstPage,()=>run(()=>this.firstPage())),gridButton(this.labels.gridPreviousPage,()=>run(()=>this.previousPage())),gridButton(this.labels.gridNextPage,()=>run(()=>this.nextPage()))];if(!this.cursor)this.pageButtons.push(gridButton(this.labels.gridLastPage,()=>run(()=>this.goToPage(Math.max(0,this.getPaging().pageCount-1)))));const navigation=h("span","cx-pager");navigation.append(this.pageButtons[0],this.pageButtons[1]);this.pageButtons.forEach((button,index)=>{const label=button.textContent;button.replaceChildren(gridIcon(["first","previous","next","last"][index]));button.setAttribute("aria-label",label);button.title=label});if(!this.cursor){const number=this.pageNumber=h("input");number.type="number";number.min="1";number.step="1";number.required=true;number.setAttribute("aria-label",this.labels.gridPageNumber);const field=gridInputLabel(this.labels.gridPageNumber,number);field.classList.add("pg");this.pageOf=h("span");field.appendChild(this.pageOf);navigation.appendChild(field)}
686
771
  navigation.append(...this.pageButtons.slice(2));const length=this.pageLengthChoice=h("select");const rpp=gridInputLabel(this.labels.gridPageLength,length);rpp.classList.add("cx-rpp");pager.append(this.pageRange,rpp,navigation);length.addEventListener("change",()=>run(()=>this.setPageLength(length.value==="all"?"all":Number(length.value))),{signal});pager.addEventListener("submit",event=>{event.preventDefault();if(this.pageNumber&&this.pageNumber.checkValidity())run(()=>this.goToPage(Number(this.pageNumber.value)- 1))},{signal});this.host.appendChild(pager);this.syncPager()},syncPager(){if(!this.pager)return;const paging=this.getPaging(),busy=!this.acceptedQuery||!!this.pendingQuery;const numbers=numberFormat(this.lang);gridCountLabel(this.pageRange,paging.matched===null?this.labels.gridCursorRange:this.labels.gridPageRange,{first:numbers.format(paging.first),last:numbers.format(paging.last),count:numbers.format(paging.matched)},/(\{first\}.*?\{last\})/);
687
772
  if(this.pageNumber){this.pageNumber.value=String(paging.pageCount?paging.pageIndex+1:1);setText(this.pageOf,fill(this.labels.gridPageOf,{page:"",pages:numbers.format(Math.max(1,paging.pageCount))}).trim());this.pageNumber.max=String(Math.max(1,paging.pageCount));this.pageNumber.disabled=busy||!paging.pageCount}
688
773
  this.pageButtons.forEach((button,index)=>{button.disabled=busy||(this.cursor?index===0?this.pageOffset===0:
@@ -692,17 +777,21 @@ throw new Error("CanopyX: cursor keys stay inside the accepted page.");if(this.p
692
777
  this.pendingQuery&&pendingPage&&pendingPage.epoch===this.queryEpoch)){const request=gridQuery(this.view,Object.assign({},this.acceptedQuery,{offset:Math.floor(index / this.pageLength) * this.pageLength, limit: this.pageSize }));
693
778
  const operation=this.navigatePageQuery(request);navigation.epoch=this.queryEpoch;await operation}
694
779
  if(this.pendingNavigation!==navigation)return;if(this.destroyed||this.pendingQuery)throw new DOMException("Grid navigation was superseded.","AbortError");if(index<this.rowBounds().first||index>this.rowBounds().last)
695
- throw new Error("CanopyX: row position is outside the accepted result page.");const epoch=this.queryEpoch,ownsFocus=focus&&this.canvas.contains(document.activeElement);this.setFocus(this.rowItem(index),ownsFocus);navigation.epoch=epoch;this.pendingNavigation=navigation;const navigationTop=this.logicalTop();const row=await this.waitForRow(index,epoch);if(this.destroyed||epoch!==this.queryEpoch||this.pendingQuery)throw new DOMException("Grid navigation was superseded.","AbortError");if(this.pendingNavigation!==navigation)return;if(focus&&(!ownsFocus||!this.canvas.contains(document.activeElement)||!this.focused||this.focused._ri!==index))return;const currentColumn=columnId?this.visibleColumns().findIndex(item=>item.id===columnId):-1;if(!focus||currentColumn<0||!row||row.status){if(this.logicalTop()===navigationTop)this.setFocus(row,focus)}else{const currentTop=this.logicalTop();this.focusCell(row,currentColumn,control);if(currentTop!==navigationTop){this.setLogicalTop(currentTop);this.render()}}}finally{if(this.pendingNavigation===navigation)this.pendingNavigation=null}}});Object.assign(DataGrid.prototype,{build(){const host=this.host,style=host.style,signal=this.lifetime.signal;const minimum=style.getPropertyValue("--cx-min-h"),priority=style.getPropertyPriority("--cx-min-h"),rowHeight=style.getPropertyValue("--cx-row-h"),rowPriority=style.getPropertyPriority("--cx-row-h"),density=host.getAttribute("data-cx-density"),maximum=style.maxHeight;this.restoreHeight=()=>{style.maxHeight=maximum;style.setProperty("--cx-min-h",minimum,priority);style.setProperty("--cx-row-h",rowHeight,rowPriority);if(density===null)host.removeAttribute("data-cx-density");else host.setAttribute("data-cx-density",density)};style.removeProperty("--cx-min-h");host.classList.add("cx-host","cx-data-grid");host.classList.toggle("cx-row-accents",!!this.view.rowAccents);host.setAttribute("data-cx-density",this.view.density||"default");if(!host.hasAttribute("data-cx-theme"))host.setAttribute("data-cx-theme","auto");style.setProperty("--cx-row-h",this.rowH+"px");this.toolbar=h("div","cx-toolbar");this.toolbar.hidden=this.opts.toolbar===false;if(this.view.search&&this.tools.search!==false)this.buildGridSearch();this.buildGridControls();this.buildGridChrome();this.buildQuerySummary();const grid=this.grid=h("div","cx-grid");grid.id=this.id;grid.tabIndex=-1;grid.setAttribute("role","grid");grid.setAttribute("aria-label",this.view.label);if(this.multi)grid.setAttribute("aria-multiselectable","true");this.head=h("div","cx-head");this.head.setAttribute("role","rowgroup");this.headRow=h("div","cx-hrow");this.headRow.setAttribute("role","row");this.headRow.setAttribute("aria-rowindex","1");this.head.appendChild(this.headRow);this.canvas=h("div","cx-canvas");this.canvas.setAttribute("role","rowgroup");grid.appendChild(this.head);grid.appendChild(this.canvas);this.notice=h("div","cx-grid-notice");this.notice.hidden=true;this.live=h("div","cx-live cx-sr-only");this.live.setAttribute("aria-live","polite");host.append(this.toolbar,this.querySummary);if(this.groupBar)host.appendChild(this.groupBar);host.append(this.notice,grid,this.live);this.buildPager();this.buildGridStatus();this.buildHeader();grid.addEventListener("scroll",()=>{this.scheduleRender();this.syncPinnedShadows();this.stateChanged()},{passive:true,signal});grid.addEventListener("click",event=>this.onClick(event),{signal});grid.addEventListener("dblclick",event=>{const element=event.target.closest(".cx-row"),row=element&&element._w;if(row&&!row.status&&!event.target.closest("a,button,input,select,textarea,[contenteditable]"))
696
- Promise.resolve().then(()=>this.openDetails(row.key)).catch(error=>this.emit("error",{error}))},{signal});grid.addEventListener("keydown",event=>this.onKey(event),{signal});this.canvas.addEventListener("focusin",event=>{const element=event.target.closest(".cx-row");if(element&&element._w&&this.pendingNavigation&&this.pendingQuery&&element._w._ri!==this.pendingNavigation.index)
780
+ throw new Error("CanopyX: row position is outside the accepted result page.");const epoch=this.queryEpoch,ownsFocus=focus&&this.canvas.contains(document.activeElement);const initial=this.rowItem(index);this.setFocus(initial,ownsFocus);navigation.epoch=epoch;this.pendingNavigation=navigation;if(focus===false&&column==null&&control==null&&initial&&!initial.status&&
781
+ !this.destroyed&&!this.dataLifetime.signal.aborted&&epoch===this.queryEpoch&&!this.pendingQuery&&
782
+ this.pendingNavigation===navigation&&this.rowCache.get(index)===initial&&this.focused===initial&&
783
+ this.rendered.get(initial)?.isConnected)return;const navigationTop=this.logicalTop();const row=await this.waitForRow(index,epoch);if(this.destroyed||epoch!==this.queryEpoch||this.pendingQuery)throw new DOMException("Grid navigation was superseded.","AbortError");if(this.pendingNavigation!==navigation)return;if(focus&&(!ownsFocus||!this.canvas.contains(document.activeElement)||!this.focused||this.focused._ri!==index))return;const currentColumn=columnId?this.visibleColumns().findIndex(item=>item.id===columnId):-1;if(!focus||currentColumn<0||!row||row.status){if(this.logicalTop()===navigationTop)this.setFocus(row,focus)}else{const currentTop=this.logicalTop();this.focusCell(row,currentColumn,control);if(currentTop!==navigationTop){this.setLogicalTop(currentTop);this.render()}}}finally{if(this.pendingNavigation===navigation)this.pendingNavigation=null}}});Object.assign(DataGrid.prototype,{build(){const host=this.host,style=host.style,signal=this.lifetime.signal;const minimum=style.getPropertyValue("--cx-min-h"),priority=style.getPropertyPriority("--cx-min-h"),rowHeight=style.getPropertyValue("--cx-row-h"),rowPriority=style.getPropertyPriority("--cx-row-h"),density=host.getAttribute("data-cx-density"),maximum=style.maxHeight;this.restoreHeight=()=>{style.maxHeight=maximum;style.setProperty("--cx-min-h",minimum,priority);style.setProperty("--cx-row-h",rowHeight,rowPriority);if(density===null)host.removeAttribute("data-cx-density");else host.setAttribute("data-cx-density",density)};style.removeProperty("--cx-min-h");host.classList.add("cx-host","cx-data-grid","cx-chrome");host.classList.toggle("cx-row-accents",!!this.view.rowAccents);host.setAttribute("data-cx-density",this.view.density||"default");if(!host.hasAttribute("data-cx-theme"))host.setAttribute("data-cx-theme","auto");style.setProperty("--cx-row-h",this.rowH+"px");this.toolbar=h("div","cx-toolbar");this.toolbar.hidden=this.opts.toolbar===false;if(this.view.search&&this.tools.search!==false)this.buildGridSearch();this.buildGridControls();this.buildGridChrome();this.buildQuerySummary();const grid=this.grid=h("div","cx-grid");grid.id=this.id;grid.tabIndex=-1;grid.setAttribute("role","grid");grid.setAttribute("aria-label",this.view.label);if(this.multi)grid.setAttribute("aria-multiselectable","true");this.head=h("div","cx-head");this.head.setAttribute("role","rowgroup");this.headRow=h("div","cx-hrow");this.headRow.setAttribute("role","row");this.headRow.setAttribute("aria-rowindex","1");this.head.appendChild(this.headRow);this.canvas=h("div","cx-canvas");this.canvas.setAttribute("role","rowgroup");grid.appendChild(this.head);grid.appendChild(this.canvas);this.notice=h("div","cx-grid-notice");this.notice.hidden=true;this.live=h("div","cx-live cx-sr-only");this.live.setAttribute("aria-live","polite");host.append(this.toolbar,this.querySummary);if(this.groupBar)host.appendChild(this.groupBar);host.append(this.notice,grid,this.live);this.buildPager();this.buildGridStatus();this.buildHeader();grid.addEventListener("scroll",()=>{this.scheduleRender();this.syncPinnedShadows();this.stateChanged()},{passive:true,signal});grid.addEventListener("click",event=>this.onClick(event),{signal});grid.addEventListener("pointerover",event=>this.cellTip(event),{signal});grid.addEventListener("dblclick",event=>{const element=event.target.closest(".cx-row"),row=element&&element._w;if(row&&!row.status&&!event.target.closest("a,button,input,select,textarea,[contenteditable]")&&!this.clickAction(element))
784
+ this.activateRow(row,false)},{signal});grid.addEventListener("keydown",event=>this.onKey(event),{signal});if(this.view.dragRows){grid.addEventListener("dragstart",event=>this.gridDragStart(event),{signal});grid.addEventListener("dragend",()=>{GRID_DRAG.item=null},{signal})}
785
+ this.canvas.addEventListener("focusin",event=>{const element=event.target.closest(".cx-row");if(element&&element._w&&this.pendingNavigation&&this.pendingQuery&&element._w._ri!==this.pendingNavigation.index)
697
786
  this.pendingNavigation=null;if(element&&element._w&&this.focused!==element._w){this.pendingNavigation=null;this.focused=element._w;this.scheduleRender()}},{signal});document.addEventListener("focusin",event=>{if(this.pendingNavigation&&this.pendingNavigation.focus&&!this.canvas.contains(event.target))this.pendingNavigation=null},{signal});this.canvas.addEventListener("focusout",event=>this.settleCell(event.relatedTarget),{signal});this.sized=this.hasGridSize();this.resizeObserver=new ResizeObserver(()=>requestAnimationFrame(()=>{if(this.destroyed)return;if(!grid.clientWidth||!grid.checkVisibility()){this.sized=false;return}
698
787
  style.setProperty("--cx-min-h",(this.toolbar.hidden?0:[...host.children].reduce((height,child)=>
699
788
  height +(child===grid?0:child.offsetHeight),this.rowH*Math.min(4,Math.max(1,this.rowCount()))+ 2))+ "px");if(!(this.sized=this.hasGridSize()))return;this.applyWidths();this.updateGridPhone();this.fitHeight();this.viewportHeight=grid.clientHeight-this.head.offsetHeight;if(this.hiddenScrollTop!==undefined){const top=this.hiddenScrollTop;delete this.hiddenScrollTop;this.setLogicalTop(top)}
700
789
  this.scheduleRender()}));this.resizeObserver.observe(grid);this.resizeObserver.observe(this.head)},setLogicalTop(top){if(!this.hasGridSize()){this.hiddenScrollTop=top;return}
701
790
  return ROW_SCROLLING.setLogicalTop.call(this,top)},logicalTop(){return this.hiddenScrollTop??ROW_SCROLLING.logicalTop.call(this)},hasGridSize(){return!!(this.grid.clientWidth&&this.grid.clientHeight&&this.grid.checkVisibility())},buildHeader(){const sorts=this.acceptedQuery?this.acceptedQuery.sort:[];this.sortState=sorts[0]||null;ROW_HELPERS.buildHeader.call(this);for(const cell of this.headCells){const index=sorts.findIndex(sort=>sort.column===cell.getAttribute("data-cx-column"));if(index>=0){cell.setAttribute("aria-sort",sorts.length===1?sorts[index].direction==="asc"?"ascending":"descending":"other");cell.setAttribute("data-cx-sort",sorts[index].direction);cell.setAttribute("data-cx-sort-priority",String(index+1));const button=cell.querySelector(".cx-sort");button.setAttribute("data-cx-sort-priority",String(index+1));if(sorts.length>1){const priority=h("sup","cx-sort-priority");setText(priority,String(index+1));button.appendChild(priority)}
702
791
  button.setAttribute("aria-label",cell.getAttribute("aria-label")+ ", "+sorts[index].direction+", " +(index+1))}}
703
- this.buildHeaderSelection?.();this.buildColumnMenus?.()},cellOf(row,column){return row.cells[column.id]},refreshRowState(element,row){setAttr(element,"aria-selected",row.status||this.view.selection==="none"?null:String(this.selected.has(row.key)))},fillRow(element,row){const kind=row.status?"status":"node";element._w=row;if(element._kind!==kind||element._layout!==this.layout){disposeRowHostCells(element);element.textContent="";element._kind=kind;element._layout=this.layout;element._attrs={};element._cls=null;element.setAttribute("role","row");element.style.height=this.rowH+"px";element.style.width=this.rowWidth+"px";element._cells=(row.status?[null]:this.cols).map((column,index)=>{const cell=this.cell(element,index,column?column.width:this.rowWidth,column?column.kind:null);if(!column)cell.setAttribute("aria-colspan",String(this.cols.length));const slot={cell,content:cell,base:cell.className +(column?.wrap?" cx-wrap":""),cls:null,title:null,badges:[],mode:"default"};slot.textSpan=cell.appendChild(h("span","cx-text"));slot.text=slot.textSpan.appendChild(document.createTextNode(""));return slot})}
792
+ this.buildHeaderSelection?.();this.buildColumnMenus?.()},cellOf(row,column){return row.cells[column.id]},refreshRowState(element,row){setAttr(element,"aria-selected",row.status||this.view.selection==="none"?null:String(this.selected.has(row.key)))},fillRow(element,row){const kind=row.status?"status":"node";element._w=row;if(element._kind!==kind||element._layout!==this.layout){disposeRowHostCells(element);element.textContent="";element._kind=kind;element._layout=this.layout;element._attrs={};element._cls=null;element.setAttribute("role","row");element.style.height=this.rowH+"px";element.style.width=this.rowWidth+"px";element._cells=(row.status?[null]:this.cols).map((column,index)=>{const cell=this.cell(element,index,column?column.width:this.rowWidth,column?column.kind:null);if(!column)cell.setAttribute("aria-colspan",String(this.cols.length));const content=index===0&&column?.render&&this.view.reporting?.selectionControls?cell.appendChild(h("span","cx-content")):cell;const slot={cell,content,base:cell.className +(column?.wrap?" cx-wrap":""),cls:null,title:null,badges:[],mode:"default"};slot.textSpan=content.appendChild(h("span","cx-text"));slot.text=slot.textSpan.appendChild(document.createTextNode(""));return slot})}
704
793
  setClass(element,"cx-row" +(row.status?" cx-status cx-"+row.status:this.autoRows?" cx-auto-row":""));if(this.autoRows){element.style.height=row.status?this.rowH+"px":"auto";this.observeDetail(element,!row.status);this.measureNeeded=true}
705
- setAttr(element,"data-cx-id",row.key||null);setAttr(element,"data-cx-status",row.status||null);setAttr(element,"aria-busy",row.status==="loading"?"true":null);this.refreshRowState(element,row);if(row.status)element._cells[0].text.data=row.status==="error"?fill(this.labels.loadError,{error:((this.pages.get(Math.floor(row._ri / this.pageSize) * this.pageSize) || {}).error || {}).message || ""
794
+ setAttr(element,"data-cx-id",row.key||null);setAttr(element,"data-cx-status",row.status||null);setAttr(element,"draggable",this.view.dragRows&&!row.status?"true":null);setAttr(element,"aria-busy",row.status==="loading"?"true":null);this.refreshRowState(element,row);if(row.status)element._cells[0].text.data=row.status==="error"?fill(this.labels.loadError,{error:((this.pages.get(Math.floor(row._ri / this.pageSize) * this.pageSize) || {}).error || {}).message || ""
706
795
  }):this.labels.loading;else for(let i=0;i<this.cols.length;i++){const slot=element._cells[i];if(slot.highlighted){slot.cls=null;slot.highlighted=false}
707
796
  const column=this.cols[i];this.paintSlot(slot,row.h,column,this.columnIndex[i],row);if(!column.render&&!slot.text.data){slot.text.data="—";slot.cell.classList.add("p-missing");slot.missing=true}
708
797
  else if(slot.missing){slot.cell.classList.remove("p-missing");slot.missing=false}
@@ -719,178 +808,190 @@ if(changed){this.total=this.rowOffsets.top(this.rowOffsets.count);this.syncScrol
719
808
  return changed},revealGridRow(row,index,rows=this){const top=rows.rowTop(index),viewport=Math.max(0,this.grid.clientHeight-this.head.offsetHeight);if(top<this.logicalTop())this.setLogicalTop(top);else if(top+Math.min(row.hpx,viewport)>this.logicalTop()+ viewport)this.setLogicalTop(top+Math.min(row.hpx,viewport)- viewport)},gridPageStep(row,direction,rows=this){const index=this.rowProjection?rows.indexOf(row):row._ri;if(!this.autoRows)return index+direction*Math.max(1,Math.floor((this.grid.clientHeight-this.head.offsetHeight)/ this.rowH));
720
809
  const proposed=rows.rowAt(Math.max(0,rows.rowTop(index)+ direction*(this.grid.clientHeight-this.head.offsetHeight)));return direction>0?Math.max(index+1,proposed):Math.min(index-1,proposed)}});const HEIGHT_RENDER=DataGrid.prototype.render;DataGrid.prototype.render=function(){const measured=HEIGHT_RENDER.call(this);if(this.sized&&this.total!==this.fitTotal){this.fitTotal=this.total;this.fitHeight()}
721
810
  return measured};DataGrid.prototype.fitHeight=function(){if(this.view.autoHeight===false||!this.sized||this.pendingQuery)return;const grid=this.grid,style=this.host.style,content=[...grid.children].reduce((sum,child)=>sum+child.offsetHeight,grid.offsetHeight-grid.clientHeight);if(this.fitInline===undefined){this.fitInline=style.maxHeight;this.fitMax=parseFloat(getComputedStyle(this.host).maxHeight)||Infinity}
722
- const value=this.rowCount()?Math.min(this.fitMax,Math.ceil(this.host.offsetHeight-grid.offsetHeight+content))+ "px":this.fitInline;if(this.host.style.maxHeight!==value)this.host.style.maxHeight=value};const FIT={};for(const name of["applyWidths","newCell","setColumnWidth","startResize","buildHeader","visibleColumns"])FIT[name]=DataGrid.prototype[name];function gridPriority(value){return["essential","normal","optional"].includes(value)||Number.isInteger(value)&&value>=0&&value<=MAX_PROTOCOL_INTEGER}
811
+ const value=this.rowCount()?Math.min(this.fitMax,Math.ceil(this.host.offsetHeight-grid.offsetHeight+content))+ "px":this.fitInline;if(this.host.style.maxHeight!==value)this.host.style.maxHeight=value};function gridPriority(value){return["essential","normal","optional"].includes(value)||Number.isInteger(value)&&value>=0&&value<=MAX_PROTOCOL_INTEGER}
723
812
  function gridRank(column){const value=column.priority;return typeof value==="number"?value:value==="optional"?10000:value==="essential"||value===undefined&&column.identity?0:5000}
724
813
  function gridLevel(column){const rank=gridRank(column);return rank<2?0:rank<10000?1:2}
725
814
  function gridOptions(column){return column.presenter||{}}
815
+ const COLUMN_DIGITS=new Map(),COLUMN_NUMERIC_CHARS="0123456789 .,:;+-−/()[]\u00a0\u202f",COLUMN_HAS_NUMERIC=/[0-9\s.,:;+\-−/()\[\]]/;
816
+ function gridNumericVariant(grid){let sample=grid.querySelector(".cx-cell"),owned=!sample;if(owned){sample=document.createElement("span");sample.className="cx-cell";sample.style.display="none";grid.appendChild(sample)}
817
+ const variant=getComputedStyle(sample).fontVariantNumeric;if(owned)sample.remove();return variant}
818
+ function gridMeasureText(context,value,variant="normal"){const text=String(value).slice(0,512),measured=context.measureText(text).width;if(!variant.includes("tabular-nums")||!COLUMN_HAS_NUMERIC.test(text))return measured;const font=context.font;let variants=COLUMN_DIGITS.get(font);if(!variants){if(COLUMN_DIGITS.size===32)COLUMN_DIGITS.delete(COLUMN_DIGITS.keys().next().value);COLUMN_DIGITS.set(font,variants=new Map())}
819
+ if(!variants.has(variant)){const probe=document.createElement("span"),glyphs=[];probe.style.cssText="all:initial;position:absolute;visibility:hidden;display:flex;white-space:pre;padding:0;border:0;max-width:none;";probe.style.font=font;probe.style.fontVariantNumeric=variant;for(const character of COLUMN_NUMERIC_CHARS)for(const numeric of[true,false]){const glyph=document.createElement("span");glyph.textContent=(numeric?"00"+character+"00":character).repeat(10);glyph.style.cssText="flex:none;white-space:pre;";probe.appendChild(glyph);glyphs.push(glyph)}
820
+ document.body.appendChild(probe);const zero=glyphs[0].getBoundingClientRect().width / 50, numeric = new Map(), plain = new Map();
821
+ for(let index=0;index<COLUMN_NUMERIC_CHARS.length;index++){const character=COLUMN_NUMERIC_CHARS[index],canvas=context.measureText(character).width;for(let mode=0;mode<2;mode++){const delta=glyphs[index*2+mode].getBoundingClientRect().width / 10 - (mode ? 0 : 4 * zero) - canvas;
822
+ if(Math.abs(delta)>0.02)(mode?plain:numeric).set(character.charCodeAt(0),delta)}}
823
+ probe.remove();variants.set(variant,{numeric:numeric.size?numeric:null,plain:plain.size?plain:null})}
824
+ const advances=variants.get(variant),digits=/[0-9]/.test(text) ? advances.numeric : advances.plain;
825
+ if(!digits)return measured;let width=measured;for(let index=0;index<text.length;index++){width +=digits.get(text.charCodeAt(index))||0}
826
+ return width}
726
827
  function gridTextual(column){return column.kind==="text"&&!["statusStrip","sparklineBars","sparklineLine","boolean","progress"].includes(gridOptions(column).kind)}
727
- function gridFitPrepare(grid){const identity=grid.columns.find(column=>column.rowHeader)||grid.columns[0];for(const column of grid.columns){prepareWidth(column);column.identity=column===identity;column.pref=column.autoWidth?0:column.width;column.basePriority=column.priority;column.declared=column.width;column.flexWeight=typeof column.flex==="number"?column.flex:0;if(column.flexWeight)column.flex=true;else if(typeof column.flex==="number")delete column.flex;column.pin??=column.pinned===undefined?column.identity?"left":"none":column.pinned||"none"}
728
- grid.columns=["left","none","right"].flatMap(side=>grid.columns.filter(column=>column.pin===side))}
729
- Object.assign(DataGrid.prototype,{visibleColumns(){return FIT.visibleColumns.call(this).filter(column=>!column.fitHidden)},newCell(index,width,kind){return FIT.newCell.call(this,index,kind?this.cols[index].renderWidth??width:width,kind)},buildHeader(){this.fitLayout();this.fitBuilding=true;try{FIT.buildHeader.call(this)}finally{this.fitBuilding=false}
730
- this.headCells.forEach((cell,index)=>cell.querySelector(".cx-resize").addEventListener("keydown",event=>{if(["ArrowLeft","ArrowRight","Home"].includes(event.key))this.fitManual(this.cols[index])},true))},startResize(event,column){if(!this.destroyed&&!event.button){this.fitManual(column);global.addEventListener("pointerup",()=>this.emit("columnResize",{column:column.id,width:column.width}),{once:true,signal:this.lifetime.signal})}
731
- return FIT.startResize.call(this,event,column)},setColumnWidth(id,width){const column=this.columns.find(item=>item.id===id);if(column&&Number.isFinite(width))column.sized=true;FIT.setColumnWidth.call(this,id,width);this.emit("columnResize",{column:id,width:column.width})},applyWidths(){this.fitLayout();const visible=this.visibleColumns(),cols=this.cols,width=this.grid.clientWidth;if(cols&&(visible.length!==cols.length||visible.some((column,index)=>column!==cols[index]))){if(!this.fitBuilding){this.buildHeader();this.syncColumnControls();this.render();return}
732
- queueMicrotask(()=>this.destroyed||this.applyWidths())}
733
- let total=0,target;for(const column of cols)total +=column.renderWidth=this.fitSize(column);for(const level of[2,1,0])if(width&&total>width){let slack=0;for(const column of cols)slack +=column.renderWidth-this.fitSize(column,level);if(!level&&slack<total-width)continue;const share=Math.min(1,(total-width)/ slack || 0); total = 0;
734
- for(const column of cols)total +=column.renderWidth -=(column.renderWidth-this.fitSize(column,level))*share}
735
- if(width&&total<width)target=this.fitSpare(cols.filter(column=>!column.fixed&&!column.sized),width-total);total=0;for(const column of cols)total +=column.renderWidth=Math.floor(column.renderWidth);if(target)target.renderWidth +=Math.min(width-total,target.maxW-target.renderWidth);FIT.applyWidths.call(this,cols.map(column=>column.renderWidth))},fitManual(column){column.width=column.renderWidth??column.width;column.sized=true},fitSize(column,level){const need=column.need??column.width;if(level===0)return column.identity&&column.priority===undefined&&!column.fixed&&!column.sized?
828
+ function gridFitColumn(column,identity){prepareWidth(column);column.identity=column===identity;column.pref=column.autoWidth?0:column.width;column.basePriority=column.priority;column.declared=column.width;column.flexWeight=typeof column.flex==="number"?column.flex:0;if(column.flexWeight)column.flex=true;else if(typeof column.flex==="number")delete column.flex}
829
+ const COLUMN_FIT={fitManual(column){column.width=column.renderWidth??column.width;column.sized=true},fitSize(column,level){const need=(column.need??column.width)+(column.reserve||0);if(level===0)return column.identity&&column.priority===undefined&&!column.fixed&&!column.sized?
736
830
  Math.max(column.minW,column.headNeed||0):column.renderWidth;return Math.round(columnWidth(column,column.fixed||column.sized?column.width:!level?Math.max(need,column.pref):level>1||!gridLevel(column)?need:
737
- Math.min(need,Math.max(column.headNeed||0,gridTextual(column)?this.fitCh||88:0))))},fitSpare(columns,spare){let target,flex=columns.filter(column=>column.flex&&column.flexWeight);const grow=(column,limit)=>{const add=column?Math.min(spare,Math.min(limit,column.maxW)- column.renderWidth):0;if(add>0){column.renderWidth +=add;spare -=add;target=column}};for(let before=0;spare>0.5&&flex.length&&before!==spare;){const weight=flex.reduce((sum,column)=>sum+column.flexWeight,0);before=spare;for(const column of flex)grow(column,column.renderWidth+before*column.flexWeight / weight);
831
+ Math.min(need,Math.max(column.headNeed||0,gridTextual(column)?this.fitCh||88:0))))},fitWidths(cols,width){let total=0,target;for(const column of cols)total +=column.renderWidth=this.fitSize(column);if(width&&total>width&&this.layoutMode&&this.layoutMode!=="fit")width=total;for(const level of[2,1,0])if(width&&total>width&&(level||cols.reduce((sum,column)=>sum+column.renderWidth-this.fitSize(column,0),0)>=total-width))
832
+ total=width+this.fitShrink(cols,total-width,level);if(width&&total<width)target=this.fitSpare(cols.filter(column=>!column.fixed&&!column.sized),width-total);total=0;for(const column of cols)total +=column.renderWidth=Math.floor(column.renderWidth);if(width>total&&!(target?.renderWidth<target?.maxW))for(const column of cols)
833
+ if(!column.fixed&&!column.sized&&column.renderWidth<column.maxW&&gridTextual(column))target=column;if(target)target.renderWidth +=Math.min(width-total,target.maxW-target.renderWidth);return cols.map(column=>column.renderWidth)},fitShrink(cols,excess,level){for(const group of[cols.filter(column=>column.flex),cols.filter(gridTextual),cols]){if(excess<=0.5)break;const floor=new Map(group.map(column=>[column,Math.min(column.renderWidth,this.fitSize(column,level))]));const cut=width=>group.reduce((sum,column)=>sum+Math.max(0,column.renderWidth-Math.max(width,floor.get(column))),0);let low=0,high=Math.max(0,...group.map(column=>column.renderWidth));for(let step=0;step<24;step++){const middle=(low+high)/ 2; if (cut(middle) >= excess) low = middle; else high = middle; }
834
+ excess -=cut(low);for(const column of group)column.renderWidth=Math.min(column.renderWidth,Math.max(low,floor.get(column)))}
835
+ return excess},fitSpare(columns,spare){let target,flex=columns.filter(column=>column.flex&&column.flexWeight);const grow=(column,limit)=>{const add=column?Math.min(spare,Math.min(limit,column.maxW)- column.renderWidth):0;if(add>0){column.renderWidth +=add;spare -=add;target=column}};for(let before=0;spare>0.5&&flex.length&&before!==spare;){const weight=flex.reduce((sum,column)=>sum+column.flexWeight,0);before=spare;for(const column of flex)grow(column,column.renderWidth+before*column.flexWeight / weight);
738
836
  flex=flex.filter(column=>column.maxW-column.renderWidth>0.5)}
739
837
  const identity=columns.find(column=>column.identity),widest=columns.filter(column=>!column.identity&&
740
838
  gridTextual(column)&&gridLevel(column)===1).sort((a,b)=>b.need-a.need)[0];grow(identity,420);if(identity&&widest)grow(widest,widest.renderWidth+spare*widest.need / (widest.need + identity.need));
741
- grow(identity,Infinity);grow(widest,Infinity);return target},fitLayout(){const width=this.grid.clientWidth,phone=this.host.clientWidth<=640,shown=FIT.visibleColumns.call(this).filter(column=>!column.responsiveHidden);if(!width)return;const key=this.pendingQuery?this.fitKey:this.queryEpoch+":"+phone;if(key!==this.fitKey||!this.fitCh){this.fitKey=key;this.fitFont=null;for(const column of this.columns){const result=this.measureGridColumn(column);column.need=result.width;column.headNeed=result.head}}
742
- for(const column of this.columns)if(!column.fixed&&!column.sized)column.width=this.fitSize(column);let total=0,remaining=shown.length;for(const column of this.columns)column.fitHidden=false;for(const column of shown)total +=this.fitSize(column,1);for(const column of shown.filter(column=>column.pin==="none"&&!column.sized&&!column.alwaysVisible&&
743
- gridLevel(column)>!phone).sort((a,b)=>gridLevel(b)- gridLevel(a)||gridRank(b)- gridRank(a)||shown.indexOf(b)- shown.indexOf(a)))
744
- if(total>width&&remaining>1){column.fitHidden=true;total -=this.fitSize(column,1);remaining--}},measureGridColumn(column){const context=this.fitContext||=document.createElement("canvas").getContext("2d"),query=this.acceptedQuery;if(!context)throw new Error("CanopyX: no canvas context.");if(!this.fitFont){const style=getComputedStyle(this.grid),head=this.headCells?.[0]&&getComputedStyle(this.headCells[0]),family=" "+style.fontFamily;this.fitFont=[style.fontWeight+" "+style.fontSize+family,"700 12.5px"+family,(head?head.fontWeight+" "+head.fontSize:"700 13px")+ family,parseFloat(getComputedStyle(this.host).getPropertyValue("--cx-cell-px"))||12];context.font=this.fitFont[0];this.fitCh=Math.ceil(context.measureText("00000000000").width)}
745
- const[,small,headFont,pad]=this.fitFont,first=column===FIT.visibleColumns.call(this)[0],options=gridOptions(column),kind=options.kind;const text=(value,font=this.fitFont[0])=>{context.font=font;return context.measureText(String(value).slice(0,512)).width};const head=Math.ceil(text(column.title,headFont)+ 2*pad +(this.host.clientWidth<=640?52:32)+(this.view.reporting?.columnReorder?24:0)+
839
+ grow(identity,Infinity);grow(widest,Infinity);return target},fitHide(shown,width,phone){let total=0,remaining=shown.length;const order=(a,b)=>gridLevel(b)- gridLevel(a)||gridRank(b)- gridRank(a)||shown.indexOf(b)- shown.indexOf(a),identity=phone&&shown.find(column=>column.identity);const hide=(column,pinned)=>!column.sized&&!column.alwaysVisible&&(column.pin==="none")!==pinned&&!(phone&&column.identity)&&gridLevel(column)>!phone;for(const column of this.columns)column.fitHidden=false;if(this.layoutMode&&this.layoutMode!=="fit")return;for(const column of shown)total +=this.fitSize(column,1);for(const column of shown.filter(column=>hide(column,false)).sort(order))
840
+ if(total>width&&remaining>1){column.fitHidden=true;total -=this.fitSize(column,1);remaining--}
841
+ if(identity&&!identity.fixed&&!identity.sized)total +=Math.min(identity.need??identity.width,(identity.indentNeed||0)+ 2*(this.fitCh||88))+(identity.reserve||0)- this.fitSize(identity,1);if(identity)for(const column of shown.filter(column=>hide(column,true)).sort(order))
842
+ if(total>width){column.fitHidden=true;total -=this.fitSize(column,1)}}};const FIT={};for(const name of["applyWidths","newCell","setColumnWidth","startResize","buildHeader","visibleColumns"])FIT[name]=DataGrid.prototype[name];function gridFitPrepare(grid){const identity=grid.columns.find(column=>column.rowHeader)||grid.columns[0];for(const column of grid.columns){gridFitColumn(column,identity);column.pin??=column.pinned===undefined?column.identity?"left":"none":column.pinned||"none"}
843
+ grid.columns=["left","none","right"].flatMap(side=>grid.columns.filter(column=>column.pin===side))}
844
+ Object.assign(DataGrid.prototype,COLUMN_FIT,{visibleColumns(){return FIT.visibleColumns.call(this).filter(column=>!column.fitHidden)},newCell(index,width,kind){return FIT.newCell.call(this,index,kind?this.cols[index].renderWidth??width:width,kind)},buildHeader(){this.fitLayout();this.fitBuilding=true;try{FIT.buildHeader.call(this)}finally{this.fitBuilding=false}
845
+ this.headCells.forEach((cell,index)=>cell.querySelector(".cx-resize").addEventListener("keydown",event=>{if(["ArrowLeft","ArrowRight","Home"].includes(event.key))this.fitManual(this.cols[index])},true))},startResize(event,column){if(!this.destroyed&&!event.button){this.fitManual(column);global.addEventListener("pointerup",()=>this.emit("columnResize",{column:column.id,width:column.width}),{once:true,signal:this.lifetime.signal})}
846
+ return FIT.startResize.call(this,event,column)},setColumnWidth(id,width){const column=this.columns.find(item=>item.id===id);if(column&&Number.isFinite(width))column.sized=true;FIT.setColumnWidth.call(this,id,width);this.emit("columnResize",{column:id,width:column.width})},applyWidths(){this.fitLayout();const visible=this.visibleColumns(),cols=this.cols,width=this.grid.clientWidth;if(cols&&(visible.length!==cols.length||visible.some((column,index)=>column!==cols[index]))){if(!this.fitBuilding){this.buildHeader();this.syncColumnControls();this.render();return}
847
+ queueMicrotask(()=>this.destroyed||this.applyWidths())}
848
+ FIT.applyWidths.call(this,this.fitWidths(cols,width))},fitLayout(){const width=this.grid.clientWidth,phone=this.host.clientWidth<=640,shown=FIT.visibleColumns.call(this).filter(column=>!column.responsiveHidden);if(!width)return;const key=this.pendingQuery?this.fitKey:this.queryEpoch+":"+phone;if(key!==this.fitKey||!this.fitCh){this.fitKey=key;this.fitFont=null;for(const column of this.columns){const result=this.measureGridColumn(column);column.need=result.width;column.headNeed=result.head}}
849
+ for(const column of this.columns){column.reserve=0;if(!column.fixed&&!column.sized)column.width=this.fitSize(column)}
850
+ this.fitHide(shown,width,phone);const first=shown.find(column=>!column.fitHidden),reserve=()=>first.reserve=Math.max(24,Math.ceil(this.fitContext.measureText("+"+this.hiddenFieldCount()).width))+ 8;if(phone&&first&&this.hiddenFieldCount()){this.fitContext.font=this.fitFont[1];reserve();this.fitHide(shown,width,phone);reserve()}},measureGridColumn(column){const context=this.fitContext||=document.createElement("canvas").getContext("2d"),query=this.acceptedQuery;if(!context)throw new Error("CanopyX: no canvas context.");if(!this.fitFont){const style=getComputedStyle(this.grid),head=this.headCells?.[0]&&getComputedStyle(this.headCells[0]),family=" "+style.fontFamily;this.fitFont=[style.fontWeight+" "+style.fontSize+family,"700 12.5px"+family,(head?head.fontWeight+" "+head.fontSize:"700 13px")+ family,parseFloat(getComputedStyle(this.host).getPropertyValue("--cx-cell-px"))||12];this.fitNumericVariant=gridNumericVariant(this.grid);context.font=this.fitFont[0];this.fitCh=Math.ceil(gridMeasureText(context,"00000000000",this.fitNumericVariant))}
851
+ const[,small,headFont,pad]=this.fitFont,first=column===FIT.visibleColumns.call(this)[0],options=gridOptions(column),kind=options.kind;const text=(value,font=this.fitFont[0],numeric=true)=>{context.font=font;return gridMeasureText(context,value,numeric?this.fitNumericVariant:"normal")};const head=Math.ceil(Math.max(24,text(column.title,headFont,false))+ 2*pad +(this.tools.columnMenus===false?8:this.host.clientWidth<=640?52:32)+(this.view.reporting?.columnReorder?28:0)+
746
852
  (query?.sort.some(sort=>sort.column===column.id)?22:0)+(gridColumnFiltered(query,column.id)?24:0)+(first&&this.selectionControls?26:0));let values=0,sampled=0;const none=column.autoSize==="none"||column.pref&&(column.wrap||["longText","tags","list"].includes(kind));if(!none&&column.autoSize!=="header")for(const row of this.rowCache.values()){if(sampled===200)break;const cell=row.h.cells[column.id],value=cellText(cell,column,this.fmt)||"—",series=gridSeries(cell).length;const quiet=options.quietTones?.includes(gridPresenterTone(options,cellValue(cell),isCellObject(cell)&&cell.tone));values=Math.max(values,kind==="statusStrip"?Math.min(series,12)*7:kind==="sparklineBars"?Math.min(series,12)*6:kind==="sparklineLine"?80:kind==="boolean"?16:
747
853
  Math.min(kind==="longText"||kind==="tags"||kind==="list"||column.wrap?320:420,text(value,kind==="enum"&&!quiet?small:undefined))+
748
- (kind==="progress"?80:kind==="enum"&&!quiet?18:kind==="state"&&!quiet?15:kind==="tags"||kind==="list"?48:0));sampled++}
854
+ (kind==="progress"?80:kind==="enum"?18:kind==="state"&&!quiet?15:kind==="tags"||kind==="list"?48:0));sampled++}
749
855
  const cap=columnWidth(column,420);const need=none?column.pref||column.width:Math.min(cap,Math.max(head,Math.ceil(values +(sampled?2*pad+4 +(first&&this.view.reporting?.selectionControls?28:0):0))));return Object.freeze({width:Math.max(column.minW,need),head:Math.min(head,cap),sampled,scope:"loaded"})},autoFitColumn(id){const column=this.columns.find(item=>item.id===id);if(!column||this.destroyed||this.pendingQuery||!this.acceptedQuery||!this.hasGridSize())throw new Error("CanopyX: auto-fit is unavailable.");this.fitFont=null;const result=this.measureGridColumn(column);this.setColumnWidth(id,result.width);return result},autoFitColumns(){return Object.freeze(Object.fromEntries(this.columns.map(column=>[column.id,this.autoFitColumn(column.id)])))},autoSizeColumns(ids=this.columns.map(column=>column.id)){if(!Array.isArray(ids)||ids.some(id=>!this.columns.some(column=>column.id===id)))throw new Error("CanopyX: name declared columns.");for(const column of this.columns)if(ids.includes(column.id)){column.sized=false;if(column.flexWeight)column.flex=true}
750
856
  this.fitKey=null;this.applyWidths();this.stateChanged()},setColumnPriority(id,priority){const column=this.columns.find(item=>item.id===id);if(!column||!gridPriority(priority))throw new Error("CanopyX: invalid column priority.");column.priority=priority;this.applyWidths();this.syncColumnControls();this.stateChanged()}});const FIT_STATE_COLUMN=gridStateColumn,FIT_STATE_COLUMNS=gridStateColumns;gridStateColumn=column=>Object.assign(FIT_STATE_COLUMN(column),{width:column.sized||column.fixed?column.width:column.declared},column.sized&&{sized:true},column.priority!==undefined&&{priority:column.priority});gridStateColumns=(grid,input)=>{const result=FIT_STATE_COLUMNS(grid,input),sources=new Map(input.map(column=>[column?.id,column]));for(const saved of result){const{sized=sources.get(saved.id)?.flex===false||undefined,priority}=sources.get(saved.id)||saved;if(sized!==undefined&&typeof sized!=="boolean"||priority!==undefined&&!gridPriority(priority))throw new Error("CanopyX: invalid saved column.");Object.assign(saved,sized&&{sized},priority!==undefined&&{priority})}
751
- return result};function gridDecoration(tone,className){if(tone!==undefined&&(typeof tone!=="string"||!/^[a-z][a-z0-9-]{0,31}$/.test(tone) || tone.endsWith("-bg")) ||
752
- className!==undefined&&(typeof className!=="string"||!className.trim()||className.length>1024||/[\u0000-\u001f\u007f-\u009f]/.test(className)))
753
- throw new Error("CanopyX: invalid highlight tone or className.")}
754
- function validateGridHighlights(view){if(view.highlightRules===undefined)return;if(!Array.isArray(view.highlightRules)||view.highlightRules.length>64)throw new Error("CanopyX: at most 64 highlight rules.");const columns=new Map(view.columns.map(column=>[column.id,column])),budget={predicates:0};for(const rule of view.highlightRules){if(!rule||
857
+ return result};function validateGridHighlights(view){if(view.highlightRules===undefined)return;if(!Array.isArray(view.highlightRules)||view.highlightRules.length>64)throw new Error("CanopyX: at most 64 highlight rules.");const columns=new Map(view.columns.map(column=>[column.id,column])),budget={predicates:0};for(const rule of view.highlightRules){if(!rule||
755
858
  rule.target!==undefined&&!["row","cell"].includes(rule.target)||rule.tone===undefined&&rule.className===undefined)
756
859
  throw new Error("CanopyX: invalid highlight rule.");const{column,operator,values}=rule;gridPredicate({column,operator,values},columns,budget);gridDecoration(rule.tone,rule.className)}}
757
860
  DataGrid.prototype.paintGridHighlights=function(element,row){if(!this.view.highlightRules?.length&&!row.h.tone&&!row.h.className&&!element._cxHighlight)return;const matches=new Map();if(!row.status)for(const rule of this.view.highlightRules||[]){const key=rule.target==="cell"?rule.column:null;if(!matches.has(key)&&gridMatches(row.h,rule,this.columns.find(column=>column.id===rule.column)))matches.set(key,rule)}
758
861
  const mark=matches.get(null),name=row.status?null:mark?.tone??row.h.tone;const tone=toneOf(name),classes=row.status?"":[row.h.className,mark?.className].filter(Boolean).join(" ");setClass(element,(element._cls??element.className)+(classes?" "+classes:"")+ tone.cls);toneColors(element,tone);setAttr(element,"data-cx-tone",name||null);if(name)element.style.setProperty("--cx-row-tone","var(--cx-tone-"+name+",var(--cx-text))");else element.style.removeProperty("--cx-row-tone");element._cxHighlight=!!(name||classes);for(const[id,rule]of matches)if(id!==null){const index=this.cols.findIndex(column=>column.id===id),slot=element._cells[index];if(!slot)continue;const base=rule.tone?slot.cell.className.replace(/(?:^|\s)cx-tone-[a-z0-9-]+|(?:^|\s)cx-toned/g, "") : slot.cell.className;
759
- slot.cell.className=base +(rule.className?" "+rule.className:"")+(rule.tone?toneOf(rule.tone).cls:"");if(rule.tone)toneColors(slot.cell,toneOf(rule.tone));slot.highlighted=true}};DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridExpandCell:"{column}: show complete text",gridCloseCell:"Close complete text"}));const GRID_PRESENTERS_BUILD=DataGrid.prototype.build;function gridPresenter(options,column,columns){const kinds=["link","icon","progress","boolean","list","longText","enum","state","change","statusStrip","sparklineBars","sparklineLine","identifier","tags","missing"];const invalid=()=>{throw new Error("CanopyX: invalid grid presenter or raw column binding.")};if(!options||typeof options!=="object"||Array.isArray(options)||!kinds.includes(options.kind))invalid();if(["progress","change"].includes(options.kind)&&column.kind!=="number"||
862
+ slot.cell.className=base +(rule.className?" "+rule.className:"")+(rule.tone?toneOf(rule.tone).cls:"");if(rule.tone)toneColors(slot.cell,toneOf(rule.tone));slot.highlighted=true}};DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridExpandCell:"{column}: show complete text",gridCloseCell:"Close complete text"}));const GRID_PRESENTERS_BUILD=DataGrid.prototype.build;function gridPresenter(options,column,columns){const kinds=["link","icon","progress","boolean","list","longText","enum","state","change","statusStrip","sparklineBars","sparklineLine","identifier","tags","missing","structured"];const invalid=()=>{throw new Error("CanopyX: invalid grid presenter or raw column binding.")};if(!options||typeof options!=="object"||Array.isArray(options)||!kinds.includes(options.kind))invalid();if(["progress","change"].includes(options.kind)&&column.kind!=="number"||
760
863
  ["boolean","list","tags","link"].includes(options.kind)&&column.kind!=="text")invalid();for(const name of["newWindow","inverted","mono","higherIsGood"])if(options[name]!==undefined&&typeof options[name]!=="boolean")invalid();for(const name of["hrefColumn","deltaColumn","secondaryColumn"])if(options[name]!==undefined){const target=columns.find(item=>item.id===options[name]);if(!target||name==="hrefColumn"&&target.kind!=="text"||name==="deltaColumn"&&target.kind!=="number")invalid()}
761
864
  if(options.kind==="change"&&!options.deltaColumn||options.kind==="icon"&&(typeof options.glyph!=="string"||!options.glyph.trim()||options.glyph.length>32))invalid();if(options.maximum!==undefined&&(!Number.isFinite(options.maximum)||options.maximum<=0||options.maximum>Number.MAX_SAFE_INTEGER))invalid();if(["list","tags"].includes(options.kind)&&(typeof options.separator!=="string"||!options.separator.length||options.separator.length>16||
762
865
  options.limit!==undefined&&(!Number.isInteger(options.limit)||options.limit<1||options.limit>16)))invalid();if((options.minimum===undefined)!==(options.scaleMaximum===undefined)||options.minimum!==undefined&&
763
- (![options.minimum,options.scaleMaximum].every(value=>Number.isFinite(value)&&Math.abs(value)<=Number.MAX_SAFE_INTEGER)||options.minimum>=options.scaleMaximum))invalid();if(options.mark!==undefined&&!['last','worst'].includes(options.mark))invalid();if(options.quietTones!==undefined&&(!Array.isArray(options.quietTones)||options.quietTones.length>16||options.quietTones.some(tone=>typeof tone!=="string"||!/^[a-z][a-z0-9-]{0,31}$/.test(tone)))) invalid();
764
- if(options.tones!==undefined&&(!options.tones||typeof options.tones!=="object"||Array.isArray(options.tones)||Object.keys(options.tones).length>512||
866
+ (![options.minimum,options.scaleMaximum].every(value=>Number.isFinite(value)&&Math.abs(value)<=Number.MAX_SAFE_INTEGER)||options.minimum>=options.scaleMaximum))invalid();if(options.kind==="structured"&&(options.display!==undefined&&!["count","chips","first"].includes(options.display)||options.limit!==undefined&&(!Number.isInteger(options.limit)||options.limit<1||options.limit>16)||
867
+ options.countText!==undefined&&(typeof options.countText!=="string"||!options.countText.includes("{count}")||options.countText.length>256)))invalid();if(options.mark!==undefined&&!['last','worst'].includes(options.mark))invalid();if(options.quietTones!==undefined&&(!Array.isArray(options.quietTones)||options.quietTones.length>16||options.quietTones.some(tone=>typeof tone!=="string"||!/^[a-z][a-z0-9-]{0,31}$/.test(tone)))) invalid();
868
+ if(options.quietStyle!==undefined&&!["muted","plain"].includes(options.quietStyle))invalid();if(options.tones!==undefined&&(!options.tones||typeof options.tones!=="object"||Array.isArray(options.tones)||Object.keys(options.tones).length>512||
765
869
  Object.entries(options.tones).some(([key,value])=>key.length>4096||typeof value!=="string")))invalid();if(options.thresholds!==undefined&&(!Array.isArray(options.thresholds)||options.thresholds.length>32||options.thresholds.some((point,index)=>
766
870
  !point||!Number.isFinite(point.upperBound)||Math.abs(point.upperBound)>Number.MAX_SAFE_INTEGER||
767
871
  typeof point.tone!=="string"||index>0&&options.thresholds[index-1].upperBound>=point.upperBound)))invalid()}
768
872
  function gridSeries(cell){return isCellObject(cell)&&cell.series||[]}
769
- function gridSeriesValid(points){return Array.isArray(points)&&points.length<=4096&&points.every(point=>point&&typeof point==="object"&&!Array.isArray(point)&&
770
- typeof point.period==="string"&&!!point.period.trim()&&point.period.length<=256&&
771
- (point.value==null||Number.isFinite(point.value)&&Math.abs(point.value)<=Number.MAX_SAFE_INTEGER)&&
772
- (point.state==null||typeof point.state==="string"&&point.state.length<=4096)&&(point.tone===undefined||typeof point.tone==="string"))}
773
- function gridPresenterTone(options,raw,fallback){if(options.tones&&Object.hasOwn(options.tones,String(raw)))return options.tones[String(raw)];return options.thresholds?.find(item=>typeof raw==="number"&&raw<=item.upperBound)?.tone||fallback||"neutral"}
873
+ const GRID_TONE_RANK={none:0,neutral:0,success:1,info:2,warning:3,danger:4},GRID_HISTORY_KINDS=["statusStrip","sparklineBars","sparklineLine"];function gridPresenterTone(options,raw,fallback){if(options.tones&&Object.hasOwn(options.tones,String(raw)))return options.tones[String(raw)];return options.thresholds?.find(item=>typeof raw==="number"&&raw<=item.upperBound)?.tone||fallback||"neutral"}
774
874
  function gridPresenterLink(raw){if(typeof raw!=="string"||raw.length>8192||/[\u0000-\u001f\u007f]/.test(raw)||!raw.trim()) return null;
775
875
  const value=raw.trim();try{const url=new URL(value,document.baseURI);if(["http:","https:","mailto:"].includes(url.protocol))return url.href;if(url.protocol==="file:"&&!/^[a-z][a-z\d+.-]*:|^[\\/]{2}|\\/i.test(value)) return url.href;
776
876
  }catch(_){/* Invalid destinations retain plain text. */ }
777
877
  return null}
778
878
  DataGrid.prototype.build=function(){for(const column of this.columns){const options=column.presenter||(column.wrap?{kind:"longText"}:null);if(options&&!column.render){column.render=gridCellRenderer(this,column,options);column.hostRenderer=false}}
779
- return GRID_PRESENTERS_BUILD.call(this)};function gridCellRenderer(grid,column,options){return(row,element)=>{const cell=row.cells[column.id],raw=cellValue(cell),text=cellText(cell,column,grid.fmt),object=isCellObject(cell);const tone=gridPresenterTone(options,raw,object?cell.tone:null);element.classList.add("cx-presented-cell");if(object&&cell.title)element.title=cell.title;const add=(value,cls="cx-presenter-text",tag="span")=>{const item=h(tag,cls);setText(item,value);element.appendChild(item);return item};const more=(value,label=grid.labels.gridShowMore)=>{const button=add(label,"cx-link cx-presenter-expand","button");button.type="button";button.setAttribute("aria-haspopup","dialog");button.setAttribute("aria-label",fill(grid.labels.gridExpandCell,{column:column.title}));button.addEventListener("click",()=>grid.showReportingText(column,value,button,row.id,options.mono));return button};if(["statusStrip","sparklineBars","sparklineLine"].includes(options.kind)){gridRenderHistory(grid,column,options,cell,element,text)}else if(raw==null&&(!object||cell.text===undefined)||text==="")add("—","p-missing");else if(options.kind==="link"){const href=gridPresenterLink(cellValue(options.hrefColumn?row.cells[options.hrefColumn]:cell));const link=add(text,"p-link",href?"a":"span");if(href){link.href=href;if(options.newWindow){link.target="_blank";link.rel="noopener noreferrer"}}}else if(options.kind==="icon"){add(options.glyph,"cx-presenter-icon").setAttribute("aria-hidden","true");add(text)}
780
- else if(options.kind==="boolean"&&typeof raw==="boolean"){const value=add(raw?"":"—",raw?"p-bool-yes"+(options.inverted?" cx-bool-bad":""):"p-bool-no");value.setAttribute("role","img");value.setAttribute("aria-label",raw?grid.labels.yes:grid.labels.no);if(raw)value.appendChild(gridIcon("check"))}else if(options.kind==="state"||options.kind==="enum"){if(options.quietTones?.includes(tone))add(text,"p-muted");else if(options.kind==="state"||tone==="success"){const state=add("","p-state"+(tone==="success"?" quiet":""));const dot=h("span","p-dot "+(tone==="success"?"quiet":tone));toneColors(dot,toneOf(tone));state.append(dot,document.createTextNode(text))}else{const pill=add(text,"p-pill "+tone);applyTone(pill,pill.className,toneOf(tone))}}else if(options.kind==="change"){const delta=cellValue(row.cells[options.deltaColumn]);if(typeof delta==="number")add((delta===0?"":delta>0?"↑ ":"↓ ")+numberFormat(grid.lang).format(Math.abs(delta)),"p-delta "+(delta===0?"flat":(delta>0)===!!options.higherIsGood?"good":"bad"));add(text)}else if(options.kind==="progress"&&typeof raw==="number"){const box=add("","p-progress"),track=h("span","track"),bar=h("span","fill "+tone),maximum=options.maximum||100;track.setAttribute("role","progressbar");track.setAttribute("aria-label",column.title);track.setAttribute("aria-valuemin","0");track.setAttribute("aria-valuemax",String(maximum));track.setAttribute("aria-valuenow",String(Math.min(maximum,Math.max(0,raw))));track.setAttribute("aria-valuetext",text);bar.style.width=Math.min(100,Math.max(0,raw/maximum*100))+"%";toneColors(bar,toneOf(tone));track.appendChild(bar);box.append(track,document.createTextNode(text));
879
+ return GRID_PRESENTERS_BUILD.call(this)};function gridCellRenderer(grid,column,options){return(row,element)=>{const cell=row.cells[column.id],raw=cellValue(cell),text=cellText(cell,column,grid.fmt),object=isCellObject(cell);const tone=gridPresenterTone(options,raw,object?cell.tone:null);element.classList.add("cx-presented-cell");if(object&&cell.title)element.title=cell.title;const add=(value,cls="cx-presenter-text",tag="span")=>{const item=h(tag,cls);setText(item,value);element.appendChild(item);return item};const more=(value,label=grid.labels.gridShowMore)=>{const button=add(label,"cx-link cx-presenter-expand","button");button.type="button";button.setAttribute("aria-haspopup","dialog");button.setAttribute("aria-label",fill(grid.labels.gridExpandCell,{column:column.title}));button.addEventListener("click",()=>grid.showReportingText(column,value,button,row.id,options.mono));return button};if(GRID_HISTORY_KINDS.includes(options.kind)){if(GRID_HISTORY_IMPL.render)GRID_HISTORY_IMPL.render(grid,column,options,cell,element,text);else grid.loadHistory()}else if(options.kind==="structured"){if(RECORD_IMPL.summary)RECORD_IMPL.summary(grid,column,options,row,element,text);else{add(text);grid.loadRecordPanel()}}else if(raw==null&&(!object||cell.text===undefined)||text==="")add("—","p-missing");else if(options.kind==="link"){const href=gridPresenterLink(cellValue(options.hrefColumn?row.cells[options.hrefColumn]:cell));const link=add(text,"p-link",href?"a":"span");if(href){link.href=href;if(options.newWindow){link.target="_blank";link.rel="noopener noreferrer"}}}else if(options.kind==="icon"){add(options.glyph,"cx-presenter-icon").setAttribute("aria-hidden","true");add(text)}
880
+ else if(options.kind==="boolean"&&typeof raw==="boolean"){const value=add(raw?"":"—",raw?"p-bool-yes"+(options.inverted?" cx-bool-bad":""):"p-bool-no");value.setAttribute("role","img");value.setAttribute("aria-label",raw?grid.labels.yes:grid.labels.no);if(raw)value.appendChild(gridIcon("check"))}else if(options.kind==="state"||options.kind==="enum"){if(options.quietTones?.includes(tone))add(text,(options.quietStyle==="plain"?"cx-presenter-text":"p-muted")+(options.kind==="enum"?" p-inset":""));else if(options.kind==="state"||tone==="success"){const state=add("","p-state"+(tone==="success"?" quiet":""));const dot=h("span","p-dot "+(tone==="success"?"quiet":tone));toneColors(dot,toneOf(tone));state.append(dot,document.createTextNode(text))}else{const pill=add(text,"p-pill "+tone);applyTone(pill,pill.className,toneOf(tone))}}else if(options.kind==="change"){const delta=cellValue(row.cells[options.deltaColumn]);if(typeof delta==="number")add((delta===0?"":delta>0?"↑ ":"↓ ")+numberFormat(grid.lang).format(Math.abs(delta)),"p-delta "+(delta===0?"flat":(delta>0)===!!options.higherIsGood?"good":"bad"));add(text)}else if(options.kind==="progress"&&typeof raw==="number"){const box=add("","p-progress"),track=h("span","track"),bar=h("span","fill "+tone),maximum=options.maximum||100;track.setAttribute("role","progressbar");track.setAttribute("aria-label",column.title);track.setAttribute("aria-valuemin","0");track.setAttribute("aria-valuemax",String(maximum));track.setAttribute("aria-valuenow",String(Math.min(maximum,Math.max(0,raw))));track.setAttribute("aria-valuetext",text);bar.style.width=Math.min(100,Math.max(0,raw/maximum*100))+"%";toneColors(bar,toneOf(tone));track.appendChild(bar);box.append(track,document.createTextNode(text));
781
881
  }else if(options.kind==="identifier"){add(text,options.mono?"p-mono":"cx-presenter-text");const secondary=options.secondaryColumn?cellText(row.cells[options.secondaryColumn],grid.columns.find(item=>item.id===options.secondaryColumn),grid.fmt):object&&cell.secondary;if(secondary)add(secondary,"p-sub")}else if(options.kind==="tags"||options.kind==="list"){const values=text.split(options.separator).map(item=>item.trim()).filter(Boolean),limit=options.kind==="tags"?3:options.limit||3;const preview=add("","cx-tag-list");element.classList.add("cx-list-cell");for(const value of values.slice(0,limit)){const tag=h("span","p-tag");setText(tag,value);preview.appendChild(tag)}
782
882
  if(values.length>limit||options.kind==="list")more(text,values.length>limit?"+"+(values.length-limit):grid.labels.gridShowMore)}else if(options.kind==="longText"){const budget=Math.max(256,Math.ceil((column.renderWidth||column.width)/7*5));
783
883
  const preview=add(text.length>budget?text.slice(0,budget).replace(/[\uD800-\uDBFF]$/,"")+" …":text,"cx-wrapped-text"+(options.mono?" p-mono":""));
784
884
  if(!grid.autoRows)element.classList.add("cx-fixed-text");if(!column.wrap||text.length>Math.max(40,(column.renderWidth||column.width)/7*4)||text.split("\n").length>4){element.classList.add("cx-clamped");more(text);}
785
885
  }else add(text);if(object)for(const badge of cell.badges||[])element.appendChild(badgeElement(badge));return true}}
786
- const GRID_TONE_RANK={none:0,neutral:0,success:1,info:2,warning:3,danger:4};function gridHistoryTone(point,options){return point.state==null&&point.value==null?"none":gridPresenterTone(options,point.value,point.tone||"success")}
787
- function gridRenderHistory(grid,column,options,cell,element,text){const series=gridSeries(cell),width=Math.max(2,(column.renderWidth||column.width)-24);if(!series.length){const missing=h("span","p-missing");setText(missing,"—");element.appendChild(missing);return}
788
- const title=point=>point.period+": "+(point.state??(point.value==null?grid.labels.gridNoData:String(point.value)));if(options.kind==="statusStrip"){const strip=h("span","p-strip");strip.setAttribute("role","img");strip.setAttribute("aria-label",column.title+": "+text);const capacity=Math.max(1,Math.floor((width+1)/3)),bucketSize=Math.max(1,Math.ceil(series.length/capacity)),buckets=[];
789
- for(let start=0;start<series.length;start+=bucketSize){const points=series.slice(start,start+bucketSize);let worst=points[0];for(const point of points)if((GRID_TONE_RANK[gridHistoryTone(point,options)]??2)>(GRID_TONE_RANK[gridHistoryTone(worst,options)]??2))worst=point;buckets.push({points,worst})}
790
- const segment=Math.max(2,Math.min(6,(width-buckets.length+1)/buckets.length));
791
- for(const{points,worst}of buckets){const tone=gridHistoryTone(worst,options),item=h("i",tone);item.style.width=segment+"px";item.title=points.length===1?title(worst):fill(grid.labels.gridPeriodRange,{from:points[0].period,to:points[points.length-1].period,state:worst.state??(worst.value==null?grid.labels.gridNoData:String(worst.value))});item.setAttribute("data-cx-periods",String(points.length));item.setAttribute("aria-hidden","true");toneColors(item,toneOf(tone));strip.appendChild(item)}
792
- element.appendChild(strip);return}
793
- const values=series.filter(point=>typeof point.value==="number");if(!values.length){setText(element,"—");element.classList.add("p-missing");return}
794
- const minimum=options.minimum??Math.min(0,...values.map(point=>point.value)),maximum=options.scaleMaximum??Math.max(...values.map(point=>point.value));const fraction=value=>Math.max(0,Math.min(1,(value-minimum)/(maximum-minimum||1)));
795
- const worse=(point,previousPoint)=>{const rank=GRID_TONE_RANK[gridHistoryTone(point,options)]??2,previous=GRID_TONE_RANK[gridHistoryTone(previousPoint,options)]??2;return rank>previous||rank===previous&&(options.higherIsGood?point.value<previousPoint.value:point.value>previousPoint.value)};let marked=values[values.length-1];if(options.mark!=="last")for(const point of values)if(worse(point,marked))marked=point;if(options.kind==="sparklineBars"){const box=h("span","p-spark"),size=Math.max(1,Math.ceil(values.length/Math.max(1,Math.floor(width/6))));
796
- box.setAttribute("role","img");box.setAttribute("aria-label",column.title+": "+text);for(let start=0;start<values.length;start+=size){const points=values.slice(start,start+size);let point=points[0];for(const sample of points)if(worse(sample,point))point=sample;const item=h("i",gridHistoryTone(point,options));item.style.height=Math.max(2,fraction(point.value)*20)+"px";item.title=points.length===1?title(point):fill(grid.labels.gridPeriodRange,{from:points[0].period,to:points[points.length-1].period,state:point.state??String(point.value)});if(points.includes(marked))item.classList.add("cx-history-mark");toneColors(item,toneOf(gridHistoryTone(point,options)));box.appendChild(item)}element.appendChild(box)}else{const ns="http://www.w3.org/2000/svg",svg=document.createElementNS(ns,"svg"),line=document.createElementNS(ns,"polyline"),mark=document.createElementNS(ns,"circle");svg.classList.add("p-line");svg.setAttribute("width","96");svg.setAttribute("height","22");svg.setAttribute("viewBox","0 0 96 22");svg.setAttribute("role","img");svg.setAttribute("aria-label",column.title+": "+text);const xy=(point,index)=>[values.length===1?48:1+index/(values.length-1)*94,20-fraction(point.value)*18];
797
- line.setAttribute("points",values.map((point,index)=>xy(point,index).join(",")).join(" "));const[x,y]=xy(marked,values.indexOf(marked));mark.setAttribute("cx",String(x));mark.setAttribute("cy",String(y));mark.setAttribute("r","2.2");mark.classList.add(gridHistoryTone(marked,options));const tooltip=document.createElementNS(ns,"title");setText(tooltip,title(marked));mark.appendChild(tooltip);svg.append(line,mark);element.appendChild(svg)}}
798
886
  Object.assign(DataGrid.prototype,{setNotice(text,error){clearTimeout(this.gridLoadingTimer);this.notice.replaceChildren();this.notice.hidden=true;this.host.insertBefore(this.notice,this.grid);this.host.classList.remove("cx-grid-awaiting");if(!this.stateCleanup){this.stateCleanup=true;this.lifetime.signal.addEventListener("abort",()=>{clearTimeout(this.gridLoadingTimer);clearTimeout(this.gridSkeletonTimer)},{once:true})}
799
887
  this.notice.className="cx-grid-notice";this.notice.setAttribute("role",error?"alert":"status");if(this.pendingQuery&&!error){const epoch=this.queryEpoch;this.gridLoadingTimer=setTimeout(()=>{if(this.destroyed||epoch!==this.queryEpoch||!this.pendingQuery)return;setText(this.live,this.labels.loading);if(!this.snapshot){this.host.classList.add("cx-grid-awaiting");this.notice.classList.add("cx-initial-skeleton");this.notice.hidden=false;this.host.insertBefore(this.notice,this.grid.nextSibling);for(let index=0;index<6;index++){const row=h("div","cx-skel");row.setAttribute("aria-hidden","true");for(const column of this.visibleColumns()){const cell=h("span"),bar=h("i");cell.style.flex=column.renderWidth||column.width;cell.appendChild(bar);row.appendChild(cell)}this.notice.appendChild(row)}}},150);return}
800
888
  if(error){this.notice.classList.add("cx-banner");const message=h("div"),title=h("b"),detail=h("span","d");setText(title,this.labels.gridLoadFailed);setText(detail,text);message.append(title,document.createTextNode(" "),detail);const retry=gridDecorateButton(gridButton(this.labels.gridRetry,()=>this.retry().catch(()=>{})),"retry",true);retry.classList.add("cx-grid-retry");this.notice.append(message,retry);this.notice.hidden=false;setText(this.live,this.labels.gridLoadFailed+" "+text);return}
801
889
  if(this.snapshot&&(this.cursor?this.cursorWindow.end===this.pageOffset:this.snapshot.matched===0)){const query=this.acceptedQuery,active=!!query.search||gridFilterCount(query)>0,title=h("h4"),reason=h("p"),actions=h("div","acts");this.host.classList.add("cx-grid-awaiting");this.notice.classList.add("cx-state");this.host.insertBefore(this.notice,this.grid.nextSibling);title.setAttribute("aria-level","2");setText(title,fill(active?this.labels.gridNoMatches:this.labels.gridEmpty,{noun:this.noun}));setText(reason,active?fill(this.labels.gridNoMatchesReason,{search:query.search||"—",count:gridFilterCount(query)}):this.view.emptyReason||this.labels.gridEmptyReason);if(active)actions.appendChild(gridButton(this.labels.gridClearAll,()=>this.setQuery(gridQueryStart(this.queryForChange(true),{search:"",filters:[],filterGroup:undefined})).catch(()=>{})));this.notice.append(title,reason,actions);this.notice.hidden=false;setText(this.live,title.textContent+" "+reason.textContent)}else if(text){setText(this.notice,text);this.notice.hidden=false}},paintGridSkeleton(element,row){if(row.status!=="loading"){element._skeleton?.remove();element._skeleton=null;return}
802
890
  row.loadingAt??=performance.now();const wait=150-(performance.now()-row.loadingAt);if(wait>0){if(!this.gridSkeletonTimer)this.gridSkeletonTimer=setTimeout(()=>{this.gridSkeletonTimer=null;if(!this.destroyed){this.version++;this.scheduleRender()}},wait);return}
803
- const cell=element._cells[0].cell;element._cells[0].text.data="";if(element._skeleton?.isConnected)return;const skeleton=element._skeleton=h("span","cx-skel");skeleton.setAttribute("aria-hidden","true");for(const column of this.cols){const span=h("span"),bar=h("i");span.style.width=column.renderWidth+"px";span.appendChild(bar);skeleton.appendChild(span)}cell.appendChild(skeleton)},buildGridStatus(){if(!this.view.statusBar)return;this.statusBar=h("div","cx-grid-status");this.statusCount=h("span");this.statusAggregates=h("span","agg");this.statusBar.setAttribute("role","status");this.statusBar.append(this.statusCount,this.statusAggregates);this.host.appendChild(this.statusBar)},async syncGridStatus(){if(!this.statusBar)return;const count=this.querySelection?this.querySelection.matched==null?null:Math.max(0,this.querySelection.matched-this.selectionExcluded.size):this.selected.size;const number=numberFormat(this.lang);gridCountLabel(this.statusCount,this.labels.gridStatusCount,{count:this.snapshot?.matched==null?"—":number.format(this.snapshot.matched),noun:this.noun,selected:count==null?"—":number.format(count)});const columns=this.columns.filter(column=>column.aggregatable),key=JSON.stringify([this.queryEpoch,this.snapshot?.revision,count,[...this.selected],this.querySelection?.query,[...(this.selectionExcluded||[])],columns.map(column=>column.id)]);if(key===this.statusAggregateKey)return;this.statusAggregateKey=key;this.statusAggregateController?.abort();this.statusAggregates.replaceChildren();if(!columns.length||count===0||!this.acceptedQuery)return;try{await loadGridFilterEditor();if(!this.destroyed&&this.statusAggregateKey===key)await this.calculateGridAggregates(columns)}
891
+ const cell=element._cells[0].cell;element._cells[0].text.data="";if(element._skeleton?.isConnected)return;const skeleton=element._skeleton=h("span","cx-skel");skeleton.setAttribute("aria-hidden","true");for(const column of this.cols){const span=h("span"),bar=h("i");span.style.width=column.renderWidth+"px";span.appendChild(bar);skeleton.appendChild(span)}cell.appendChild(skeleton)},buildGridStatus(){if(!this.view.statusBar)return;this.statusBar=h("div","cx-grid-status");this.statusCount=h("span");this.statusAggregates=h("span","agg");this.statusBar.setAttribute("role","status");this.statusBar.append(this.statusCount,this.statusAggregates);this.host.appendChild(this.statusBar)},async syncGridStatus(){if(!this.statusBar)return;const count=this.querySelection?this.querySelection.matched==null?null:Math.max(0,this.querySelection.matched-this.selectionExcluded.size):this.selected.size;const number=numberFormat(this.lang);gridCountLabel(this.statusCount,this.labels.gridStatusCount,{count:this.snapshot?.matched==null?"—":number.format(this.snapshot.matched),noun:this.noun,selected:count==null?"—":number.format(count)});const columns=this.columns.filter(column=>column.aggregatable&&column.exportable!==false),key=JSON.stringify([this.queryEpoch,this.snapshot?.revision,count,[...this.selected],this.querySelection?.query,[...(this.selectionExcluded||[])],columns.map(column=>column.id)]);if(key===this.statusAggregateKey)return;this.statusAggregateKey=key;this.statusAggregateController?.abort();this.statusAggregates.replaceChildren();if(!columns.length||count===0||!this.acceptedQuery)return;try{await loadGridFilterEditor();if(!this.destroyed&&this.statusAggregateKey===key)await this.calculateGridAggregates(columns)}
804
892
  catch(error){if(!this.destroyed&&this.statusAggregateKey===key)setText(this.statusAggregates,this.labels.gridNoData)}}});Object.assign(DataGrid.prototype,{setFocus(row,focus){this.pendingNavigation=null;if(!row||this.indexOf(row)<0||this.destroyed)return;this.focused=row;this.revealGridRow(row,row._ri);if(this.render()){this.revealGridRow(row,row._ri);this.render()}
805
- if(focus){const element=this.rendered.get(row);if(element)element.focus({preventScroll:true})}},onClick(event){const element=event.target.closest(".cx-row"),row=element&&element._w;if(!row||event.defaultPrevented||event.target.closest("a,button,input,select,textarea,[contenteditable]"))return;this.setFocus(row,true);if(row.status){if(row.status==="error")this.retry().catch(()=>{});return}
806
- const link=this.view.rowAction==="link"&&element.querySelector("a[href]");if(link)link.dispatchEvent(new MouseEvent("click",{bubbles:true,cancelable:true,ctrlKey:event.ctrlKey,metaKey:event.metaKey,shiftKey:event.shiftKey}));else if(this.phone||this.view.rowAction==="details")Promise.resolve().then(()=>this.openDetails(row.key)).catch(error=>this.emit("error",{error}))},onKey(event){const element=event.target.closest(".cx-row"),row=element&&element._w;if(!row||event.defaultPrevented||event.isComposing||event.altKey)return;const cells=this.rowCells(element),navigation=this.pendingNavigation;const pendingPage=navigation&&navigation.focus&&navigation.epoch===this.queryEpoch&&this.pendingQuery&&
893
+ if(focus){const element=this.rendered.get(row);if(element)element.focus({preventScroll:true})}},onClick(event){const element=event.target.closest(".cx-row"),row=element&&element._w,link=event.target.closest("a.p-link");if(row&&link&&!this.emit("link",{id:row.key,row:gridFreeze(JSON.parse(JSON.stringify(row.h))),href:link.href,column:this.visibleColumns()[this.rowCells(element).findIndex(cell=>cell.contains(link))]?.id}))event.preventDefault();if(!row||event.defaultPrevented||event.target.closest("a,button,input,select,textarea,[contenteditable]"))return;this.setFocus(row,true);if(row.status){if(row.status==="error")this.retry().catch(()=>{});return}
894
+ const action=event.detail<2&&this.clickAction(element);if(action)this.activateRow(row,false,action!==true&&action,event)},clickAction(element){return this.view.rowAction==="link"&&element.querySelector("a[href]")||this.phone||this.view.rowAction==="details"},activateRow(row,keyboard,link,event={}){if(this.emit("rowActivate",{id:row.key,row:gridFreeze(JSON.parse(JSON.stringify(row.h))),keyboard}))link?
895
+ link.dispatchEvent(new MouseEvent("click",{bubbles:true,cancelable:true,ctrlKey:event.ctrlKey,metaKey:event.metaKey,shiftKey:event.shiftKey})):
896
+ Promise.resolve().then(()=>this.openDetails(row.key)).catch(error=>this.emit("error",{error}))},onKey(event){const element=event.target.closest(".cx-row"),row=element&&element._w;if(!row||event.defaultPrevented||event.isComposing||event.altKey)return;const cells=this.rowCells(element),navigation=this.pendingNavigation;const pendingPage=navigation&&navigation.focus&&navigation.epoch===this.queryEpoch&&this.pendingQuery&&
807
897
  this.pendingPageNavigation&&this.pendingPageNavigation.epoch===this.queryEpoch&&this.focused===row;let column=cells.findIndex(cell=>cell.contains(event.target));if(pendingPage||row.status&&navigation&&navigation.epoch===this.queryEpoch&&navigation.index===row._ri&&navigation.focus)
808
- column=this.visibleColumns().findIndex(item=>item.id===navigation.columnId);const columnCount=row.status&&column>=0?this.visibleColumns().length:cells.length;const cell=column>=0?cells[column]:null,control=cell&&event.target!==cell?event.target:null;if(event.key==="Enter"&&!control&&!row.status&&column<0){event.preventDefault();Promise.resolve().then(()=>this.openDetails(row.key)).catch(error=>this.emit("error",{error}));return}
898
+ column=this.visibleColumns().findIndex(item=>item.id===navigation.columnId);const columnCount=row.status&&column>=0?this.visibleColumns().length:cells.length;const cell=column>=0?cells[column]:null,control=cell&&event.target!==cell?event.target:null;if(event.key==="Enter"&&!control&&!row.status&&column<0){event.preventDefault();this.activateRow(row,true,this.view.rowAction==="link"&&!event.ctrlKey&&!event.metaKey&&element.querySelector("a[href]"));return}
809
899
  if(!control&&!row.status&&(event.ctrlKey||event.metaKey)){if(event.key.toLowerCase()==="c"&&!(global.getSelection()||"").toString()){event.preventDefault();Promise.resolve().then(()=>this.copyFocused(row,cell)).catch(error=>this.emit("error",{error}));return}
810
- if(event.key==="Enter"){event.preventDefault();Promise.resolve().then(()=>this.openDetails(row.key)).catch(error=>this.emit("error",{error}));return}}
900
+ if(event.key==="Enter"){event.preventDefault();this.activateRow(row,true);return}}
811
901
  const controls=cell?cellControls(cell):[];if(control&&control.matches(OWN_ARROWS)){if(event.key!=="Escape")return;this.focusCell(row,column,null);cell._cxAt=controls.indexOf(control);event.preventDefault();event.stopPropagation();return}
812
902
  const at=control?controls.indexOf(control):cell&&cell._cxAt>=0&&cell._cxAt<controls.length?cell._cxAt:-1;let index=pendingPage?navigation.index:row._ri,nextColumn=column,nextControl=null;switch(event.key){case "ArrowUp":index--;break;case "ArrowDown":index++;break;case "PageUp":index=this.gridPageStep({...row,_ri:index},-1);break;case "PageDown":index=this.gridPageStep({...row,_ri:index},1);break;case "Home":if(event.ctrlKey||event.metaKey)index=this.cursor?this.rowBounds().first:0;else nextColumn=0;break;case "End":if(event.ctrlKey||event.metaKey)index=this.rowCount()- 1;else nextColumn=columnCount-1;break;case "ArrowLeft":
813
903
  if(at>0)nextControl=at-1;else if(column>0){nextColumn--;nextControl=-1}
814
904
  else{this.setFocus(row,true);event.preventDefault();return}
815
905
  break;case "ArrowRight":
816
- if(at>=0&&at<controls.length-1)nextControl=at+1;else nextColumn=Math.min(columnCount-1,column+1);break;case "Escape":this.setFocus(row,true);event.preventDefault();return;case "F2":
817
- if(column>=0){this.setFocus(row,true);event.preventDefault();return}
906
+ if(at>=0&&at<controls.length-1)nextControl=at+1;else nextColumn=Math.min(columnCount-1,column+1);break;case "Escape":this.gridPickKey(event,row,column);this.setFocus(row,true);event.preventDefault();return;case "F2":
907
+ if(this.gridPickKey(event,row,column))return;if(column>=0){this.setFocus(row,true);event.preventDefault();return}
818
908
  case "Enter":
819
909
  if(row.status){if(row.status==="error")this.retry().catch(()=>{})}
820
910
  else if(!control)this.focusCell(row,Math.max(0,column),Math.max(0,at),true);else return;event.preventDefault();return;case " ":
821
911
  if(control||row.status||this.view.selection==="none")return;{const next=this.multi?new Set(this.selected):new Set();if(next.has(row.key))next.delete(row.key);else next.add(row.key);this.setSelection([...next])}
822
912
  event.preventDefault();return;default:return}
823
- index=Math.max(this.cursor?this.rowBounds().first:0,Math.min(this.rowCount()- 1,index));this.focusResult(index,true,nextColumn,nextControl).catch(error=>{if(error.name!=="AbortError")this.emit("error",{error})});event.preventDefault();event.stopPropagation()}});function gridStateColumn(column){return{id:column.id,width:column.width,hidden:!!column.hidden}}
913
+ index=Math.max(this.cursor?this.rowBounds().first:0,Math.min(this.rowCount()- 1,index));this.focusResult(index,true,nextColumn,nextControl).catch(error=>{if(error.name!=="AbortError")this.emit("error",{error})});event.preventDefault();event.stopPropagation()}});const GRID_DRAG={item:null,picked:null};function gridDragItem(grid,row){const header=grid.columns.find(column=>column.rowHeader)||grid.columns.find(column=>!column.hidden)||grid.columns[0];const cells=Object.create(null);for(const[id,cell]of Object.entries(row.h.cells))cells[id]=cellValue(cell)??null;const minutes=Number.isInteger(cells.minutes)&&cells.minutes>=5&&cells.minutes<=10080?cells.minutes:null;return Object.freeze({source:"grid",view:grid.view.id,id:row.key,title:cellText(row.h.cells[header.id],header,grid.fmt)||row.key,minutes,allDay:false,cells:Object.freeze(cells)})}
914
+ Object.assign(DataGrid.prototype,{gridDragStart(event){const element=event.target.closest?.(".cx-row"),row=element?._w;if(!this.view.dragRows||!row||row.status)return;const item=GRID_DRAG.item=gridDragItem(this,row);event.dataTransfer.setData("application/x-canopyx-row",JSON.stringify(item));event.dataTransfer.setData("text/plain",item.title);event.dataTransfer.effectAllowed="copyMove"},gridPickKey(event,row,column){if(!this.view.dragRows||row.status||column>=0)return false;if(event.key==="Escape"&&GRID_DRAG.picked){GRID_DRAG.picked=null;return false}
915
+ if(event.key!=="F2")return false;GRID_DRAG.picked=gridDragItem(this,row);setText(this.live,fill(this.labels.gridRowPicked,{title:GRID_DRAG.picked.title}));event.preventDefault();return true}});function gridStateColumn(column){return{id:column.id,width:column.width,hidden:!!column.hidden}}
824
916
  function gridStateColumns(grid,input){if(!Array.isArray(input))throw new Error("CanopyX: invalid columns.");const seen=new Set(),declared=new Map(grid.columns.map(column=>[column.id,column]));const columns=input.map(column=>readStateColumn(column,seen)).filter(column=>declared.has(column.id));for(const column of columns)column.width=columnWidth(declared.get(column.id),column.width);for(const column of grid.columns)if(!seen.has(column.id))columns.push(gridStateColumn(column));if(!columns.some(column=>!column.hidden))throw new Error("CanopyX: keep at least one column visible.");return columns}
825
917
  Object.assign(DataGrid.prototype,{prepareState(){this.storageKey=this.view.stateKey?"cx:grid:"+this.view.stateKey+":"+this.view.id:null;this.savedViews=new Map();this.defaultState={version:1,view:this.view.id,columns:this.columns.map(gridStateColumn),query:gridQuery(this.view,gridQueryStart(this.view.query)),timeZone:this.local?"local":"utc",selection:[],scrollTop:0};if(this.paged)this.defaultState.paging={pageLength:this.pageLength,offset:0};const stored=this.readStoredState();if(stored&&Array.isArray(stored.views)){for(const entry of stored.views.slice(0,20)){try{this.savedViews.set(this.viewName(entry.name),this.readGridState(entry.state))}catch(_){/* Removed or invalid saved views are skipped. */ }
826
918
  }}
827
- try{this.initialState=stored&&stored.current?this.readGridState(stored.current):null}catch(_){this.initialState=null}},initializeState(){return this.initialState?this.setState(this.initialState):this.setQuery(this.view.query||{})},getState(){const query=this.getQuery()||this.defaultState.query;const state={version:1,view:this.view.id,columns:this.columns.map(gridStateColumn),query:JSON.parse(JSON.stringify({...query,offset:this.cursor?undefined:0,cursor:undefined,revision:undefined})),timeZone:this.local?"local":"utc",selection:[...this.selected],scrollTop:this.grid?this.logicalTop():0};if(this.paged)state.paging={pageLength:this.pageLength,offset:this.cursor?0:this.pageOffset,...this.pageAll?{allRows:true}:{}};return state},readGridState(state){if(!state||state.version!==1||state.view!==this.view.id||!["utc","local"].includes(state.timeZone)||
919
+ try{this.initialState=stored&&stored.current?this.readGridState(stored.current):null}catch(_){this.initialState=null}},initializeState(){const ready=this.initialState?this.setState(this.initialState):this.setQuery(this.view.query||{});return this.view.formatRules||this.view.formatting?Promise.all([ready,this.loadFormatting().catch(error=>this.emit("error",{error}))]).then(([result])=>result):ready},getState(){const query=this.getQuery()||this.defaultState.query;const state={version:1,view:this.view.id,columns:this.columns.map(gridStateColumn),query:JSON.parse(JSON.stringify({...query,offset:this.cursor?undefined:0,cursor:undefined,revision:undefined})),timeZone:this.local?"local":"utc",selection:[...this.selected],scrollTop:this.grid?this.logicalTop():0};if(this.paged)state.paging={pageLength:this.pageLength,offset:this.cursor?0:this.pageOffset,...this.pageAll?{allRows:true}:{}};if(this.formatEdited)state.formatRules=JSON.parse(JSON.stringify(this.formatRules));return state},readGridState(state){if(!state||state.version!==1||state.view!==this.view.id||!["utc","local"].includes(state.timeZone)||
828
920
  !Number.isFinite(state.scrollTop)||state.scrollTop<0||state.scrollTop>MAX_PROTOCOL_INTEGER*this.rowH||!Array.isArray(state.selection))
829
- throw new Error("CanopyX: invalid grid state.");const columns=gridStateColumns(this,state.columns);const selection=[...new Set(state.selection.map(id=>{requireIdentifier(id,"state selection ID");return id}))];if(this.view.selection==="none"&&selection.length||!this.multi&&selection.length>1)throw new Error("CanopyX: invalid state selection.");const input=state.query;if(!input||typeof input!=="object"||!Array.isArray(input.sort)||!Array.isArray(input.filters))throw new Error("CanopyX: invalid state query.");const known=id=>this.columns.some(column=>column.id===id);const query=gridQuery(this.view,gridQueryStart(input,{sort:input.sort.filter(sort=>sort&&known(sort.column)),filters:input.filters.filter(filter=>filter&&known(filter.column))}),true);const parsed={version:1,view:this.view.id,columns,query,timeZone:state.timeZone,selection,scrollTop:state.scrollTop};if(this.paged||state.paging){const paging=state.paging||this.defaultState.paging;if(!paging||!Number.isInteger(paging.pageLength)||paging.pageLength<1||paging.pageLength>Math.max(10000,this.view.source.kind==="inline"?this.view.rows.length:0)||paging.allRows!==undefined&&(typeof paging.allRows!=="boolean"||paging.allRows&&this.view.source.kind!=="inline")||
921
+ throw new Error("CanopyX: invalid grid state.");const columns=gridStateColumns(this,state.columns);const selection=[...new Set(state.selection.map(id=>{requireIdentifier(id,"state selection ID");return id}))];if(this.view.selection==="none"&&selection.length||!this.multi&&selection.length>1)throw new Error("CanopyX: invalid state selection.");const input=state.query;if(!input||typeof input!=="object"||!Array.isArray(input.sort)||!Array.isArray(input.filters))throw new Error("CanopyX: invalid state query.");const known=id=>this.columns.some(column=>column.id===id);const query=gridQuery(this.view,gridQueryStart(input,{sort:input.sort.filter(sort=>sort&&known(sort.column)),filters:input.filters.filter(filter=>filter&&known(filter.column)),[LINK_IMPL.queryOwn]:true}),true);const parsed={version:1,view:this.view.id,columns,query,timeZone:state.timeZone,selection,scrollTop:state.scrollTop,formatRules:state.formatRules,calendar:state.calendar};if(this.paged||state.paging){const paging=state.paging||this.defaultState.paging;if(!paging||!Number.isInteger(paging.pageLength)||paging.pageLength<1||paging.pageLength>Math.max(10000,this.view.source.kind==="inline"?(this.view.rowData?.count??this.view.rows.length):0)||paging.allRows!==undefined&&(typeof paging.allRows!=="boolean"||paging.allRows&&this.view.source.kind!=="inline")||
830
922
  !Number.isInteger(paging.offset)||paging.offset<0||paging.offset>MAX_PROTOCOL_INTEGER||this.cursor&&paging.offset!==0)
831
923
  throw new Error("CanopyX: invalid paged view state.");if(this.paged){parsed.paging={pageLength:paging.pageLength,offset:paging.offset,...paging.allRows?{allRows:true}:{}};if(!state.paging)parsed.scrollTop=0}else parsed.scrollTop=Math.min(MAX_PROTOCOL_INTEGER*this.rowH,paging.offset*this.rowH+state.scrollTop)}
832
- return parsed},async setState(input){const state=this.readGridState(input);clearTimeout(this.searchTimer);this.searchTimer=null;const pending=this.setQuery({...state.query,offset:state.paging?state.paging.offset:0},state.paging),epoch=this.queryEpoch;this.restoringEpoch=epoch;try{await pending;if(this.destroyed||epoch!==this.queryEpoch)throw new DOMException("Grid state was superseded.","AbortError");this.applyStatePresentation(state);return this.getQuery()}finally{if(this.restoringEpoch===epoch){this.restoringEpoch=null;this.stateChanged()}}},applyStatePresentation(state){if(this.cancelResize)this.cancelResize();const current=new Map(this.columns.map(column=>[column.id,column]));this.columns=state.columns.map(saved=>{const column=current.get(saved.id);return Object.assign(column,{sized:false,priority:column.basePriority},saved)});this.setTimeZone(state.timeZone);this.setSelection(state.selection);this.buildHeader();this.syncColumnControls();this.setLogicalTop(state.scrollTop);this.render()},setColumnOrder(ids){if(!Array.isArray(ids)||ids.length!==this.columns.length||new Set(ids).size!==ids.length||
924
+ return parsed},async setState(input){const state=this.readGridState(input);const rules=state.formatRules!==undefined?JSON.parse(JSON.stringify(state.formatRules)):this.formatEdited?this.view.formatRules||[]:undefined;const before=this.queryEpoch;if(rules!==undefined)await this.validateFormatRules(rules);if(this.destroyed||before!==this.queryEpoch)throw new DOMException("Grid state was superseded.","AbortError");state.formatRules=rules;clearTimeout(this.searchTimer);this.searchTimer=null;const pending=this.setQuery({...state.query,offset:state.paging?state.paging.offset:0},state.paging),epoch=this.queryEpoch;this.restoringEpoch=epoch;try{await pending;if(this.destroyed||epoch!==this.queryEpoch)throw new DOMException("Grid state was superseded.","AbortError");await this.applyStatePresentation(state);return this.getQuery()}finally{if(this.restoringEpoch===epoch){this.restoringEpoch=null;this.stateChanged()}}},applyStatePresentation(state){if(this.cancelResize)this.cancelResize();const current=new Map(this.columns.map(column=>[column.id,column]));this.columns=state.columns.map(saved=>{const column=current.get(saved.id);return Object.assign(column,{sized:false,priority:column.basePriority},saved)});this.setTimeZone(state.timeZone);this.setSelection(state.selection);this.buildHeader();this.syncColumnControls();this.setLogicalTop(state.scrollTop);this.render();if(state.formatRules!==undefined||this.formatEdited)return this.setFormatRules(state.formatRules||this.view.formatRules||[])},setColumnOrder(ids){if(!Array.isArray(ids)||ids.length!==this.columns.length||new Set(ids).size!==ids.length||
833
925
  ids.some(id=>!this.columns.some(column=>column.id===id)))throw new Error("CanopyX: name each column once.");if(this.cancelResize)this.cancelResize();const columns=new Map(this.columns.map(column=>[column.id,column]));this.columns=ids.map(id=>columns.get(id));this.columns.forEach((column,index,all)=>{if(column.pin==="left"&&all.slice(0,index).some(item=>item.pin!=="left")||column.pin==="right"&&all.slice(index+1).some(item=>item.pin!=="right"))column.pin="none"});this.buildHeader();this.syncColumnControls();this.scheduleRender();this.stateChanged()},viewName(name){if(typeof name!=="string"||!name.trim()||name.trim().length>100)throw new Error("CanopyX: view names need 1 to 100 nonblank characters.");return name.trim()},getSavedViews(){return[...this.savedViews.keys()]},saveView(name){name=this.viewName(name);if(!this.getQuery())throw new Error("CanopyX: saving needs an accepted query.");if(!this.savedViews.has(name)&&this.savedViews.size>=20)throw new Error("CanopyX: maximum 20 saved views.");this.savedViews.set(name,this.getState());this.writeStoredState(this.getState());this.syncSavedViews();if(this.savedChoice)this.savedChoice.value=name},applySavedView(name){const state=this.savedViews.get(this.viewName(name));if(!state)throw new Error("CanopyX: the saved view does not exist.");return this.setState(state)},removeSavedView(name){this.savedViews.delete(this.viewName(name));this.writeStoredState(this.getState());this.syncSavedViews()},resetView(){return this.setState(this.defaultState)},readStoredState(){if(!this.storageKey)return null;try{return JSON.parse(global.sessionStorage.getItem(this.storageKey))}catch(_){return null}},writeStoredState(state){if(!this.storageKey||this.destroyed)return;try{global.sessionStorage.setItem(this.storageKey,JSON.stringify({current:state,views:[...this.savedViews].map(([name,value])=>({name,state:value}))}))}
834
926
  catch(_){/* A disabled or full storage leaves the current session usable. */ }
835
- }});function gridCountLabel(element,label,values,marks=/(\{count\}|\{selected\})/){
927
+ }});const GRID_HISTORY_IMPL={},GRID_HISTORY=definePart(DataGrid,"history","history presenters",()=>({GRID_HISTORY_IMPL,h,setText,fill,gridSeries,gridPresenterTone,toneColors,toneOf,GRID_TONE_RANK}));const GRID_HISTORY_INIT=DataGrid.prototype.initializeState;DataGrid.prototype.loadHistory=function(){return this.historyLoad||=loadPart(GRID_HISTORY).then(()=>{this.version++;this.scheduleRender()},error=>this.emit("error",{error}))};DataGrid.prototype.initializeState=function(){const ready=GRID_HISTORY_INIT.call(this);return this.columns.some(column=>GRID_HISTORY_KINDS.includes(column.presenter?.kind))?Promise.all([ready,this.loadHistory()]).then(([result])=>result):ready};const GRID_CALENDAR=definePart(DataGrid,"calendar","calendar",()=>({DataGrid,h,setText,fill,plural,gridButton,gridIcon,gridFreeze,cellText,cellValue,isCellObject,gridQuery,gridQueryRows,gridResultShape,gridPresenterTone,validateGridRows,validateGridPage,validateGridCursorPage,validStructuralCount,checkDocument,numberFormat,loadPart,HostCellContext,disposeHostSlot,PROTOCOL}));definePart(DataGrid,"timeline","resource timeline",()=>({DataGrid,TreeGrid,h,setText,fill,numberFormat}));definePart(DataGrid,"calendar-recurrence","calendar recurrence",()=>({DataGrid}));definePart(DataGrid,"calendar-edit","calendar editing",()=>({DataGrid,h,setText,fill,cellValue,GRID_DRAG}));definePart(DataGrid,"calendar-ics","iCalendar",()=>({DataGrid,h,fill,cellText}));const GRID_CALENDAR_API=["getCalendar","setCalendarView","setCalendarDate","setCalendarZoom","calendarStep","calendarToday","getCalendarEvents","exportCalendar","openCalendarEvent","clearCalendarSelection","setCalendarSource"];for(const name of GRID_CALENDAR_API)DataGrid.prototype[name]=function(...args){return this.loadCalendar().then(()=>this[name](...args))};DataGrid.prototype.loadCalendar=function(){return this.calLoad||=loadPart(GRID_CALENDAR).then(()=>this.destroyed||this.calParts()).then(()=>this.destroyed||this.startCalendar()).catch(error=>{this.calLoad=null;this.emit("error",{error});throw error})};const GRID_CALENDAR_INIT=DataGrid.prototype.initializeState;DataGrid.prototype.initializeState=function(){const start=()=>GRID_CALENDAR_INIT.call(this);return this.view.calendar?this.loadCalendar().then(start,start):start()};const CAL_PRESETS={basic:{toolbar:false,details:"popover",views:["month","week","day","agenda"]},standard:{toolbar:["search","filters","export"],details:"popover",print:true,views:["month","week","day","days","agenda","year"]},scheduler:{toolbar:["search","filters","views","export"],details:"drawer",print:true,editable:true,create:true,views:["month","week","day","agenda"]}};const CAL_FIELDS=["allDay","description","tone","resource","recurrence","exceptions","source"];const CAL_TITLES={allDay:"All day",description:"Description",tone:"Tone",resource:"Resource",recurrence:"Repeats",exceptions:"Exceptions",source:"Source"};const CAL_OWN=new Set(["events","preset","id","label","views","view","date","timeZone","firstDay","toolbar","details","editable","create","drop","resources","timeline","live","facets","drawer","export","print","sources","calendar","links"]);const calPad=(value,size=2)=>String(value).padStart(size,"0");function calInstantText(value,field,allDay){if(value instanceof Date&&Number.isFinite(value.getTime()))
928
+ return allDay?calPad(value.getFullYear(),4)+ "-"+calPad(value.getMonth()+ 1)+ "-"+calPad(value.getDate())+ "T00:00:00Z":value.toISOString().replace(".000Z","Z");if(typeof value==="string"&&/^\d{4}-\d\d-\d\d$/.test(value)) return value + "T00:00:00Z";
929
+ if(typeof value==="string"&&/^\d{4}-\d\d-\d\dT\d\d:\d\d(:\d\d(\.\d+)?)?(Z|[+-]\d\d:\d\d)$/.test(value)) return allDay ? value.slice(0, 10) + "T00:00:00Z" : value;
930
+ const parsed=typeof value==="string"&&/^\d{4}-\d\d-\d\dT\d\d:\d\d/.test(value) ? new Date(value) : null;
931
+ if(parsed&&Number.isFinite(parsed.getTime()))return calInstantText(parsed,field,allDay);throw new Error("CanopyX: a calendar event's "+field+" is a Date or an ISO date or date and time.")}
932
+ function createCalendar(host,options={}){const preset=CAL_PRESETS[options.preset??"basic"];if(!preset)throw new Error("CanopyX: calendar presets are basic, standard and scheduler.");const events=options.events??[];if(!Array.isArray(events)||events.length>100000)throw new Error("CanopyX: calendar events are an array of at most 100,000 events.");const hooks={},own={};for(const[name,value]of Object.entries(options.calendar||{}))(typeof value==="function"?hooks:own)[name]=value;const pick=name=>own[name]!==undefined?own[name]:options[name]!==undefined?options[name]:preset[name];const editable=!!pick("editable"),create=pick("create"),drop=!!pick("drop"),used=new Set(),ids=new Set();const rows=events.map((event,index)=>{if(!event||typeof event!=="object"||typeof event.title!=="string")throw new Error("CanopyX: calendar event " +(index+1)+ " needs a title.");const id=event.id===undefined?"e" +(index+1):String(event.id);if(ids.has(id))throw new Error("CanopyX: calendar event ids are unique; '"+id+"' repeats.");ids.add(id);const allDay=event.allDay??(typeof event.start==="string"&&event.start.length===10);const cells={title:event.title,start:calInstantText(event.start,"start",allDay)};if(event.end!=null)cells.end=calInstantText(event.end,"end",allDay);if(allDay){cells.allDay=true;used.add("allDay")}
933
+ for(const field of CAL_FIELDS)if(field!=="allDay"&&event[field]!=null){cells[field]=String(event[field]);used.add(field)}
934
+ return{id,cells}});if(options.resources)used.add("resource");if(pick("sources"))used.add("source");if(drop||create&&!(typeof create==="object"&&create.allDay===false))used.add("allDay");const writable=editable||!!create||drop;const columns=[{id:"title",title:"Title",kind:"text",rowHeader:true,sortable:true,editable:writable},{id:"start",title:"Start",kind:"datetime",sortable:true,editable:writable},{id:"end",title:"End",kind:"datetime",sortable:true,editable:writable},...CAL_FIELDS.filter(field=>used.has(field)).map(field=>({id:field,title:CAL_TITLES[field],kind:"text",hidden:field!=="description",editable:writable&&(field==="resource"||field==="allDay"||field==="recurrence"||field==="exceptions")}))];let toolbar=pick("toolbar");if(options.export===true&&toolbar!==true)toolbar=toolbar===false?["export"]:toolbar.includes("export")?toolbar:[...toolbar,"export"];if(options.export===false&&toolbar!==false)toolbar=(toolbar===true?["search","filters","columns","views","more"]:toolbar.filter(item=>item!=="export"));if(Array.isArray(toolbar)&&!toolbar.length)toolbar=false;const calendar={start:"start",end:"end",title:"title",views:pick("views"),toolbar,details:options.drawer===true?"drawer":options.drawer===false?"popover":pick("details")};for(const field of CAL_FIELDS)if(used.has(field))calendar[field]=field;for(const name of["view","date","timeZone","firstDay","sources"])if(options[name]!==undefined)calendar[name]=options[name];if(editable)calendar.editable=typeof pick("editable")==="object"?pick("editable"):true;if(create)calendar.create=create;if(drop)calendar.drop=true;if(pick("print"))calendar.print=true;const ownViews=own.views!==undefined||options.views!==undefined,addTimeline=()=>{if(!ownViews&&!calendar.views.includes("timeline"))calendar.views=[...calendar.views,"timeline"]};if(options.resources){const list=options.resources,tree=Array.isArray(list)?{protocol:PROTOCOL,type:"view",id:"resources",label:"Resources",rowHeight:34,pageSize:200,selection:"single",columns:[{id:"name",title:"Resource",kind:"text",width:200}],source:{kind:"inline"},roots:calResourceRoots(list)}:list;calendar.resources={view:tree};if(options.timeline!==false&&(options.timeline||options.preset==="scheduler"))addTimeline();if(options.preset==="scheduler")calendar.capacity={thresholds:[{at:3,tone:"warning"},{at:5,tone:"danger"}]}}else if(options.timeline)addTimeline();for(const[name,value]of Object.entries(own))if(!["toolbar","editable","create","drop"].includes(name))calendar[name]=value;if(calendar.toolbar===true)delete calendar.toolbar;if(calendar.details==="drawer")delete calendar.details;const search=calendar.toolbar===undefined||calendar.toolbar!==false&&calendar.toolbar.includes("search");const view={protocol:PROTOCOL,type:"grid",id:options.id??"calendar",label:options.label??"Calendar",rowHeight:34,pageSize:200,selection:"single",search,columns,source:{kind:"inline"},rows,calendar};if(options.live)view.live=options.live===true?{}:options.live;if(options.facets)view.facets=Array.isArray(options.facets)?{columns:options.facets}:options.facets;const hostOptions=Object.fromEntries(Object.entries(options).filter(([name])=>!CAL_OWN.has(name)));if(options.links)hostOptions.linkEvents=true;if(Object.keys(hooks).length)hostOptions.calendar=hooks;return createDataGrid(host,{...hostOptions,view})}
935
+ function calResourceRoots(list){const nodes=new Map(list.map(item=>[String(item.id),{id:String(item.id),label:String(item.label??item.id),cells:{name:String(item.label??item.id)},expanded:true}])),roots=[];for(const item of list){const node=nodes.get(String(item.id)),parent=item.parent==null?null:nodes.get(String(item.parent));if(parent)(parent.children||=[]).push(node);else roots.push(node)}
936
+ return roots}
937
+ const GRID_EDITING=definePart(DataGrid,"editing","editing",()=>({DataGrid,h,setText,fill,gridButton,gridDecorateButton,GRID_ICONS,cellValue,cellText,isCellObject,gridFreeze,gridMomentFromParts,numberFormat,gridQuery,foldText,requireIdentifier,checkDocument,validateGridRows,inlineGridSource,PROTOCOL,boundedPattern,validateGridFacetPage,RECORD_IMPL}));const GRID_EDIT_INIT=DataGrid.prototype.initializeState;const GRID_EDIT_API=["beginEdit","endEdit","setCellValue","validate","commitRow","commitChanges","insertRow","isEditing","hasChanges","undo","redo","canUndo","canRedo","cancelEdit","revertChanges","getChanges","setReadOnly"];GRID_EDIT_API.forEach((name,index)=>{DataGrid.prototype[name]=index<7?function(...args){return this.loadEditing().then(()=>this[name](...args))}:index<15?()=>false:()=>[]});DataGrid.prototype.setReadOnly=function(value){this.editReadOnly=!!value};DataGrid.prototype.loadEditing=function(){return this.editLoad||=loadPart(GRID_EDITING).then(()=>this.startEditing()).catch(error=>{this.editLoad=null;this.emit("error",{error});throw error})};DataGrid.prototype.initializeState=function(){const ready=GRID_EDIT_INIT.call(this);return this.columns.some(column=>column.editable)?Promise.all([ready,this.loadEditing().catch(()=>{})]).then(([result])=>result):ready};DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridFacetPanel:"Filter by",gridFacetPanelHide:"Hide the filter panel",gridFacetPanelShow:"Show the filter panel",gridFacetShowMore:"Show {count} more",gridFacetShowFewer:"Show fewer",gridFacetEmptyValue:"(Empty)",gridFacetFrom:"From",gridFacetTo:"To",gridFacetHistogram:"Distribution of {column}",gridFacetBin:"{from} to {to}: {count} rows",gridFacetNoChart:"Too many values to chart",gridFacetClear:"Clear {column}",gridFacetCustom:"Another condition filters this column.",gridFacetResult:"{count} matching rows",gridFacetSelected:"{count} selected",gridFacetValueCount:"{value}, {count} rows",gridFacetCollapse:"Collapse {column}",gridFacetExpand:"Expand {column}",gridFacetInvalid:"Enter a valid value.",gridFacetReversed:"From must not be after To."}));const GRID_FACETS=definePart(DataGrid,"facets","facet panel",()=>({DataGrid,h,setText,fill,gridButton,gridIcon,gridQueryStart,gridQueryRows,gridColumnProjection,gridScalar,gridOrder,cellText,foldText,numberFormat,gridCellRenderer,gridFreeze}));Object.assign(DataGrid.prototype,{loadFacets(){return this.facetLoad||=loadPart(GRID_FACETS).then(()=>this.destroyed||this.startFacets()).catch(error=>{this.facetLoad=null;throw error})},setFacetPanelOpen(open){return this.loadFacets().then(()=>this.setFacetPanelOpen(open))},getFacetPanel(){return gridFreeze({open:this.facetOpen??null,columns:(this.facetColumns||[]).map(facet=>facet.column)})}});const FACET_STATE={get:DataGrid.prototype.getState,read:DataGrid.prototype.readGridState,apply:DataGrid.prototype.applyStatePresentation,init:DataGrid.prototype.initializeState};Object.assign(DataGrid.prototype,{getState(){const state=FACET_STATE.get.call(this);if((this.view.facets||this.opts.facets)&&this.facetOpen!==undefined)state.facets={open:this.facetOpen};return state},readGridState(input){const state=FACET_STATE.read.call(this,input);if(input.facets!==undefined){const facets=input.facets;if(!facets||typeof facets!=="object"||Array.isArray(facets)||Object.keys(facets).some(key=>key!=="open")||typeof facets.open!=="boolean")
938
+ throw new Error("CanopyX: invalid saved facet panel state.");if(this.view.facets||this.opts.facets)state.facets={open:facets.open}}
939
+ return state},applyStatePresentation(state){const applied=FACET_STATE.apply.call(this,state);if(state.facets&&state.facets.open!==this.facetOpen&&!this.destroyed){if(this.facetPanel)this.setFacetPanelOpen(state.facets.open).catch(error=>this.emit("error",{error}));else this.facetOpen=state.facets.open}
940
+ return applied},initializeState(){if(this.initialState?.facets)this.facetOpen=this.initialState.facets.open;const ready=FACET_STATE.init.call(this);return this.view.facets||this.opts.facets?Promise.all([ready,this.loadFacets().catch(error=>this.emit("error",{error}))]).then(([result])=>result):ready}});const GRID_FORMAT=definePart(DataGrid,"formatting","conditional formatting",()=>({DataGrid,h,setText,setClass,fill,gridFreeze,gridDateInputMoment,cellValue,cellText,foldText,toneOf,numberFormat,gridButton,gridOption,gridInputLabel,gridIcon,gridDecorateButton,GRID_REPORTING,loadPart,definePart,announceMenu,boundedPattern,wildcardMatch,svgIcon}));Object.assign(DataGrid.prototype,{loadFormatting(){return this.formatLoad||=loadPart(GRID_FORMAT).then(()=>this.startFormatting(),error=>{this.formatLoad=null;throw error})},setFormatRules(rules){return this.loadFormatting().then(()=>this.setFormatRules(rules))},validateFormatRules(rules){return this.loadFormatting().then(()=>this.validateFormatRules(rules))},openFormatRules(){return this.loadFormatting().then(()=>this.openFormatRules())},getFormatRules(){return gridFreeze(JSON.parse(JSON.stringify(this.formatRules||this.view.formatRules||[])))}});DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridLiveOrderChanged:"Order changed",gridLiveResort:"Re-sort",gridLiveNewData:"New data",gridLiveRefresh:"Refresh",gridLiveOrderHint:"Rows keep their places while data changes. Re-sort to move them where the sort puts them.",gridLiveChanged:"Rows changed: {added} added, {updated} updated, {removed} removed",gridLiveSorted:"Rows moved to their sorted places.",gridLiveRecordRemoved:"This record was removed."}));const GRID_LIVE=definePart(DataGrid,"live","live data",()=>({DataGrid,h,setText,fill,gridButton,gridIcon,gridQuery,gridResultShape,gridQueryRows,gridCompare,gridOrder,validateGridRows,validateGridPage,validateGridCursorPage,gridFreeze,requireIdentifier,checkDocument,cellValue,PROTOCOL,GRID_REPORTING,VIEW_MODES,inlineGridSource}));const GRID_LIVE_API=["applyTransaction","addRows","updateRows","removeRows","setRows","flushTransactions","refreshRows","resort","poll"];for(const name of GRID_LIVE_API)DataGrid.prototype[name]=function(...args){return this.loadLive().then(()=>this[name](...args))};Object.assign(DataGrid.prototype,{loadLive(){return this.liveLoad||=loadPart(GRID_LIVE).then(()=>this.destroyed||this.startLive()).catch(error=>{this.liveLoad=null;throw error})},getLiveState(){return this.liveStatus?this.liveStatus():gridFreeze({orderChanged:false,newData:false,pending:0,polling:false,revision:this.snapshot?.revision??null})}});const GRID_LIVE_INIT=DataGrid.prototype.initializeState;DataGrid.prototype.initializeState=function(){const ready=GRID_LIVE_INIT.call(this);return this.view.live&&this.opts.live!==false||this.opts.live?Promise.all([ready,this.loadLive().catch(error=>this.emit("error",{error}))]).then(([result])=>result):ready};const GRID_PAGER_IMPL={},GRID_PAGER=definePart(DataGrid,"pager","pager controls",()=>({DataGrid,GRID_PAGER_IMPL,h,setText,fill,gridButton,gridOption,numberFormat,gridCountLabel}));const GRID_PAGER_INIT=DataGrid.prototype.initializeState;DataGrid.prototype.initializeState=function(){const ready=GRID_PAGER_INIT.call(this);return!this.pager||!this.view.pager||GRID_PAGER_IMPL.installed?ready:Promise.all([ready,loadPart(GRID_PAGER).then(()=>this.destroyed||this.buildPager())
941
+ .catch(error=>{this.pager.hidden=this.opts.toolbar===false;this.emit("error",{error})})]).then(([result])=>result)};DataGrid.prototype.parts["record-panel"]=RECORD_PART;RECORD_IMPL.grid={DataGrid,gridCellRenderer,createDataGrid,gridFreeze,gridQuery,gridResultShape,validatePresenter:gridPresenter};DataGrid.prototype.openRecord=TreeGrid.prototype.openRecord;DataGrid.prototype.loadRecordPanel=function(){return this.recordLoad||=loadRecordPanel().then(()=>{this.version++;this.scheduleRender()},error=>this.emit("error",{error}))};DataGrid.prototype.recordNeeded=function(row){return!!this.view.record||this.columns.some(column=>column.presenter?.kind==="structured")||
942
+ !!row&&Object.values(row.cells).some(cell=>isCellObject(cell)&&RECORD_STRUCTURE.some(name=>cell[name]!==undefined))};const GRID_RECORD_INIT=DataGrid.prototype.initializeState;DataGrid.prototype.initializeState=function(){const ready=GRID_RECORD_INIT.call(this);return this.columns.some(column=>column.presenter?.kind==="structured")?Promise.all([ready,this.loadRecordPanel()]).then(([result])=>result):ready};const GRID_DATA_IMPL={},GRID_DATA=definePart(DataGrid,"data","row data",()=>({GRID_DATA_IMPL,PROTOCOL,validateGridRows,inlineGridSource,loadSidecar}));function rowDataSource(grid){let inner=null;const source={reset:()=>{inner=null},query:(query,options)=>
943
+ (inner||=loadPart(GRID_DATA).then(()=>GRID_DATA_IMPL.source(grid,source)).catch(error=>{inner=null;throw error})).then(rows=>rows.query(query,options))};return source}
944
+ DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridCellRangeSelected:"{rows} rows × {columns} columns selected",gridCellRangesSelected:"{ranges} ranges selected, {cells} cells",gridCellRangeCleared:"Cell selection cleared",gridCopyCells:"Copy cells",gridCopyCellsWithHeaders:"Copy cells with headers",gridPasteCells:"Paste",gridSelectAllCells:"Select all cells",gridCellsCopied:"Copied {rows} rows × {columns} columns",gridCellsPasted:"Pasted {count} cells",gridPasteRefused:"{count} cells were not pasted",gridPasteReadOnly:"This cell cannot be changed.",gridPasteType:"“{value}” is not a valid value for {column}.",gridPasteReport:"Paste report",gridPasteCell:"Row {row}, {column}: {reason}",gridFillHandle:"Drag to fill the cells below",gridCellsFilled:"Filled {count} cells",gridCellMenu:"Cell actions",gridClearCellRanges:"Clear cell selection"}));const GRID_PART_CONTEXT=()=>({DataGrid,h,setText,fill,columnWidth,foldText,gridButton,gridIcon,gridDecorateButton,cellText,cellValue,isCellObject,numberFormat,gridFreeze,gridMomentFromParts,requireIdentifier,loadGridFilterEditor,CELL_CONTROLS,reachable,gridExportField});const GRID_COLUMN_GROUPS=definePart(DataGrid,"column-groups","column groups",GRID_PART_CONTEXT);const GRID_CELL_RANGE=definePart(DataGrid,"cell-range","cell ranges",GRID_PART_CONTEXT);const GRID_PART_API=[["column-groups","getColumnGroups","setColumnGroupVisible"],["cell-range","getCellRanges","setCellRanges","clearCellRanges","selectAllCells","copyCellRanges","getCellRangeText","pasteCells"]];const gridPartLoader=(part,start)=>function(){return(this.partLoads||={})[start]||=loadPart(part).then(()=>this.destroyed||this["start"+start]()).catch(error=>{this.partLoads[start]=null;throw error})};DataGrid.prototype.loadColumnGroups=gridPartLoader(GRID_COLUMN_GROUPS,"ColumnGroups");DataGrid.prototype.loadCellRanges=gridPartLoader(GRID_CELL_RANGE,"CellRanges");for(const[part,read,...actions]of GRID_PART_API){const load=part==="cell-range"?"loadCellRanges":"loadColumnGroups";DataGrid.prototype[read]=()=>[];for(const name of actions){const stub=DataGrid.prototype[name]=function(...args){return this[load]().then(()=>{if(DataGrid.prototype[name]===stub)throw new Error("CanopyX: the "+part+" part did not install "+name+".");return this[name](...args)})}}}
945
+ const GRID_PARTS_INIT=DataGrid.prototype.initializeState;DataGrid.prototype.initializeState=function(){const ready=GRID_PARTS_INIT.call(this),range=this.opts.rangeSelection??this.view.rangeSelection;const parts=[this.view.columnGroups?.length&&this.loadColumnGroups(),range&&range.enabled!==false&&this.loadCellRanges()].filter(Boolean);return parts.length?Promise.all([ready,...parts.map(part=>part.catch(error=>this.emit("error",{error})))]).then(([result])=>result):ready};function gridButton(text,run){const button=h("button","cx-button");button.type="button";setText(button,text);button.onclick=event=>{button.focus({preventScroll:true});run(event)};return button}
946
+ let keyboardInput=false;if(typeof document!=="undefined"){document.addEventListener("keydown",()=>{keyboardInput=true},true);document.addEventListener("pointerdown",()=>{keyboardInput=false},true)}
947
+ const focusDrawerTitle=title=>{if(keyboardInput&&title===document.activeElement&&!title.matches(":focus-visible"))title.blur();title.toggleAttribute("data-cx-pointer-focus",!keyboardInput);if(!keyboardInput)title.addEventListener("blur",()=>title.removeAttribute("data-cx-pointer-focus"),{once:true});title.focus({preventScroll:true,focusVisible:keyboardInput})};function gridInputLabel(text,input){const label=h("label","cx-grid-field");label.appendChild(document.createTextNode(text));label.appendChild(input);return label}
948
+ function gridOption(value,text){const option=h("option");option.value=value;setText(option,text);return option}
949
+ const MENU_EVENT="canopyx:closemenus",announceMenu=source=>document.dispatchEvent(new CustomEvent(MENU_EVENT,{detail:{source}}));const TOOLBAR_PANELS={startMenus(){this.controlFlyouts=[];this.disposeControls=()=>this.controlFlyouts.forEach(flyout=>flyout.dispose());let pressed=null;for(const type of["mouseup","click","keydown","pointercancel"])document.addEventListener(type,()=>{pressed=null},{capture:true,signal:this.lifetime.signal});for(const type of["pointerdown","focusin",MENU_EVENT])document.addEventListener(type,event=>{if(type==="pointerdown")pressed=event.target;const node=type==="focusin"&&pressed?.isConnected&&event.target.contains(pressed)?pressed:event.detail?.source||event.target;if(type!==MENU_EVENT||event.target.contains(this.host))this.closeMenusOutside(node)},{signal:this.lifetime.signal})},closeMenusOutside(node){for(const details of this.toolbar.querySelectorAll(".cx-columns[open]"))if(!details.contains(node))details.open=false;const popover=this.gridPopover;if(popover&&!popover.panel.contains(node)&&!popover.anchor?.contains(node))this.closeGridPopover(false)},closeMenus(){this.closeMenusOutside(document)},controlPanel(title,className,onOpen){const details=h("details","cx-columns "+className),summary=h("summary","cx-btn"),panel=h("div","cx-columns-list cx-grid-panel cx-pop");summary.setAttribute("role","button");if(className!=="cx-grid-filters")summary.classList.add("ghost");setText(summary,title);details.append(summary,panel);this.toolbar.appendChild(details);summary.prepend(gridIcon(className.slice(8).replace("rs","r")));panel.popover="manual";const flyout=watchFlyout(details,panel,()=>details.open);this.controlFlyouts.push(flyout);details.addEventListener("toggle",()=>{if(details.open){announceMenu(details);if(panel.isConnected)panel.showPopover?.();if(onOpen)onOpen(panel);flyout.position()}else{panel.hidePopover?.();for(const inner of panel.querySelectorAll("details[open]"))inner.open=false}});details.addEventListener("keydown",event=>{if(event.key==="Escape"&&details.open){details.open=false;summary.focus();event.preventDefault();event.stopPropagation()}});return{details,panel}},buildSavedViews(){const panel=this.controlPanel(this.labels.gridViews,"cx-grid-views").panel;const choice=this.savedChoice=h("select"),name=h("input");name.type="text";name.maxLength=100;choice.setAttribute("aria-label",this.labels.gridChooseView);panel.appendChild(choice);panel.appendChild(gridInputLabel(this.labels.gridViewName,name));const error=h("p","cx-grid-control-error");error.setAttribute("role","status");const run=action=>{try{Promise.resolve(action()).then(()=>setText(error,""),failure=>setText(error,failure.message))}
950
+ catch(failure){setText(error,failure.message)}};panel.appendChild(gridButton(this.labels.gridApply,()=>run(()=>this.applySavedView(choice.value))));panel.appendChild(gridButton(this.labels.gridSaveView,()=>run(()=>this.saveView(name.value))));panel.appendChild(gridButton(this.labels.gridRemoveView,()=>run(()=>this.removeSavedView(choice.value))));panel.appendChild(error);this.syncSavedViews()},syncSavedViews(){if(!this.savedChoice)return;const selected=this.savedChoice.value;this.savedChoice.textContent="";this.savedChoice.appendChild(gridOption("",this.labels.gridChooseView));this.getSavedViews().forEach(name=>this.savedChoice.appendChild(gridOption(name,name)));if(this.savedViews.has(selected))this.savedChoice.value=selected},placeMenus(phone){if(this.overflow)for(const selector of[".cx-grid-views",".cx-grid-export"]){const item=this.toolbar.querySelector(selector);if(!item)continue;item.open=false;if(phone)this.overflow.panel.appendChild(item);else this.toolbar.insertBefore(item,selector===".cx-grid-views"?this.slots?.afterViews||this.spacer:this.overflow.details)}}};function gridCountLabel(element,label,values,marks=/(\{count\}|\{selected\})/){
836
951
  element.replaceChildren(...label.split(marks).map((part,index)=>{if(index%2===0)return document.createTextNode(fill(part,values));const bold=h("b");setText(bold,fill(part,values));return bold}))}
837
952
  DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridResultCount:"{count} of {total} rows",gridClearQuery:"Clear search and filters",gridRemoveSearch:"Clear search: {search}",gridGroupedFilters:"Grouped filters ({count} conditions)",gridRemoveGroup:"Clear grouped filters",gridRemoveCondition:"Remove condition: {condition}"}));Object.assign(DataGrid.prototype,{buildGridSearch(){const search=h("div","cx-search"),input=this.searchInput=h("input","cx-search-input"),signal=this.lifetime.signal;input.type="search";input.placeholder=this.labels.searchPlaceholder;input.setAttribute("aria-label",this.labels.searchPlaceholder);const update=()=>{clearTimeout(this.searchTimer);this.searchTimer=null;if(this.destroyed||this.searchComposing)return;if(this.queryForChange().search===input.value)return;this.setQuery(gridQueryStart(this.queryForChange(),{search:input.value})).catch(()=>{})};GRID_REPORTING.search?.(this,update);const schedule=()=>{clearTimeout(this.searchTimer);this.searchTimer=null;if(this.searchComposing)return;if(this.searchDelayMs===0)update();else this.searchTimer=setTimeout(update,this.searchDelayMs)};input.addEventListener("compositionstart",()=>{this.searchComposing=true;clearTimeout(this.searchTimer);this.searchTimer=null},{signal});input.addEventListener("compositionend",()=>{this.searchComposing=false;schedule()},{signal});input.addEventListener("input",event=>{if(!event.isComposing)schedule()},{signal});input.addEventListener("keydown",event=>{if(event.key==="Enter"&&!this.searchComposing&&!event.isComposing&&event.keyCode!==229){event.preventDefault();update()}},{signal});const shortcut=h("kbd");setText(shortcut,"/");search.append(gridIcon("search"),input,shortcut);this.toolbar.appendChild(search);this.host.addEventListener("keydown",event=>{if(event.key==="/"&&!event.ctrlKey&&!event.metaKey&&!event.altKey&&!event.target.closest("input,textarea,select,[contenteditable]")){event.preventDefault();input.focus()}},{signal})},buildQuerySummary(){this.querySummary=h("div","cx-grid-query-summary");this.resultCount=h("span","cx-grid-result-count");this.resultCount.setAttribute("role","status");this.queryChips=h("div","cx-grid-query-chips");this.querySummary.append(this.resultCount,this.queryChips);this.querySummary.hidden=true;if(this.tools.count===false)this.resultCount.remove()},syncQuerySummary(){if(!this.acceptedQuery||!this.querySummary)return;const query=this.acceptedQuery,active=document.activeElement,focused=this.queryChips.contains(active);const key=focused&&active.getAttribute("data-cx-condition");const numbers=numberFormat(this.lang);this.querySummary.hidden=!query.search&&!gridFilterCount(query);this.syncGridChrome();gridCountLabel(this.resultCount,this.snapshot.matched===null?this.labels.gridUnknownCount:this.snapshot.total===null?
838
- this.labels.gridMatchedCount:this.view.noun?this.labels.gridResultNoun:this.labels.gridResultCount,{count:numbers.format(this.snapshot.matched),total:numbers.format(this.snapshot.total),noun:this.view.noun||this.labels.gridRows});this.queryChips.textContent="";const apply=patch=>{this.setQuery(gridQueryStart(this.queryForChange(true),patch)).catch(()=>{})};const chip=(text,label,id,run)=>{const button=gridButton(text+" ×",()=>{button.focus({preventScroll:true});run()});button.classList.add("cx-grid-chip");button.title=text;button.setAttribute("aria-label",label);button.setAttribute("data-cx-condition",id);this.queryChips.appendChild(button)};if(query.search)chip(query.search,fill(this.labels.gridRemoveSearch,{search:query.search}),"search",()=>{if(this.searchInput)this.searchInput.value="";apply({search:""})});query.filters.forEach((filter,index)=>{const column=this.columns.find(item=>item.id===filter.column),name=GRID_FILTER_LABELS[filter.operator];const condition=column.title+" " +(this.labels[name]||name)+(filter.values.length?" "+filter.values.join(", "):"");chip(condition,fill(this.labels.gridRemoveCondition,{condition}),"filter-"+index,()=>{const filters=this.queryForChange().filters.slice(),at=filters.findIndex(item=>JSON.stringify(item)===JSON.stringify(filter));if(at>=0){filters.splice(at,1);apply({filters})}})});if(query.filterGroup){const count=group=>group.filters.length+group.groups.reduce((total,child)=>total+count(child),0);chip(fill(this.labels.gridGroupedFilters,{count:numbers.format(count(query.filterGroup))}),this.labels.gridRemoveGroup,"group",()=>{if(JSON.stringify(this.queryForChange().filterGroup)===JSON.stringify(query.filterGroup))apply({filterGroup:undefined})})}
953
+ this.labels.gridMatchedCount:this.view.noun?this.labels.gridResultNoun:this.labels.gridResultCount,{count:numbers.format(this.snapshot.matched),total:numbers.format(this.snapshot.total),noun:this.view.noun||this.labels.gridRows});this.queryChips.textContent="";const apply=patch=>{this.setQuery(gridQueryStart(this.queryForChange(true),patch)).catch(()=>{})};const chip=(text,label,id,run)=>{const button=gridButton("",()=>{button.focus({preventScroll:true});run()});button.classList.add("cx-grid-chip");filterChipContent(button,text);button.title=text;button.setAttribute("aria-label",label);button.setAttribute("data-cx-condition",id);this.queryChips.appendChild(button)};if(query.search)chip(query.search,fill(this.labels.gridRemoveSearch,{search:query.search}),"search",()=>{if(this.searchInput)this.searchInput.value="";apply({search:""})});query.filters.forEach((filter,index)=>{const column=this.columns.find(item=>item.id===filter.column),name=GRID_FILTER_LABELS[filter.operator];const condition=column.title+" " +(this.labels[name]||name)+(filter.values.length?" "+filter.values.join(", "):"");chip(condition,fill(this.labels.gridRemoveCondition,{condition}),"filter-"+index,()=>{const filters=this.queryForChange().filters.slice(),at=filters.findIndex(item=>JSON.stringify(item)===JSON.stringify(filter));if(at>=0){filters.splice(at,1);apply({filters})}})});if(query.filterGroup){const count=group=>group.filters.length+group.groups.reduce((total,child)=>total+count(child),0);chip(fill(this.labels.gridGroupedFilters,{count:numbers.format(count(query.filterGroup))}),this.labels.gridRemoveGroup,"group",()=>{if(JSON.stringify(this.queryForChange().filterGroup)===JSON.stringify(query.filterGroup))apply({filterGroup:undefined})})}
839
954
  if(query.search||query.filters.length||query.filterGroup){const clear=gridButton(this.labels.gridClearAll,()=>{clear.focus({preventScroll:true});if(this.searchInput)this.searchInput.value="";apply({search:"",filters:[],filterGroup:undefined})});clear.classList.add("cx-link");clear.setAttribute("aria-label",this.labels.gridClearQuery);clear.setAttribute("data-cx-condition","clear");this.queryChips.appendChild(clear)}
840
- if(focused){const target=[...this.queryChips.children].find(button=>button.getAttribute("data-cx-condition")===key);(target||this.queryChips.lastElementChild||this.searchInput||this.grid).focus({preventScroll:true})}}});DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridFilters:"Filters",gridColumn:"Column",gridOperator:"Condition",gridValueType:"Raw value type",gridText:"Text",gridBoolean:"Boolean",gridRemoveFilter:"Remove {column} filter",gridApply:"Apply",gridClearFilters:"Clear all filters",gridFitAllColumns:"Fit all columns",gridViews:"Views",gridViewName:"View name",gridSaveView:"Save view",gridRemoveView:"Remove view",gridResetView:"Reset view",gridChooseView:"Choose a saved view",gridInvalidFilter:"Enter a valid filter value.",gridContains:"contains",gridStartsWith:"starts with",gridIn:"is any of",gridIsNull:"is missing",gridIsNotNull:"is present",gridFindColumn:"Find a column…",gridShowAllColumns:"Show all",gridResetColumns:"Reset columns",gridReorderColumn:"{column}: drag, or press Alt+Up or Alt+Down to move",gridPinnedLeft:"Pinned left",gridPinnedRight:"Pinned right"}));function gridButton(text,run){const button=h("button","cx-button");button.type="button";setText(button,text);button.onclick=event=>{button.focus({preventScroll:true});run(event)};return button}
841
- function gridInputLabel(text,input){const label=h("label","cx-grid-field");label.appendChild(document.createTextNode(text));label.appendChild(input);return label}
842
- function gridOption(value,text){const option=h("option");option.value=value;setText(option,text);return option}
843
- Object.assign(DataGrid.prototype,{buildGridControls(){this.controlFlyouts=[];this.buildGridFilters();if(this.tools.columns!==false)this.buildGridColumns();if(this.tools.views!==false){this.buildSavedViews();this.toolbar.querySelector(".cx-grid-views .cx-grid-panel").appendChild(gridButton(this.labels.gridResetView,()=>this.resetView().catch(()=>{})))}
844
- this.buildExportControls();this.disposeControls=()=>this.controlFlyouts.forEach(flyout=>flyout.dispose());document.addEventListener("pointerdown",event=>{for(const details of this.toolbar.children)if(details.open&&!details.contains(event.target))details.open=false},{signal:this.lifetime.signal})},controlPanel(title,className,onOpen){const details=h("details","cx-columns "+className),summary=h("summary","cx-btn"),panel=h("div","cx-columns-list cx-grid-panel cx-pop");summary.setAttribute("role","button");if(className!=="cx-grid-filters")summary.classList.add("ghost");setText(summary,title);details.append(summary,panel);this.toolbar.appendChild(details);summary.prepend(gridIcon(className.slice(8).replace("rs","r")));panel.popover="manual";const flyout=watchFlyout(details,panel,()=>details.open);this.controlFlyouts.push(flyout);details.addEventListener("toggle",()=>{if(details.open){if(panel.isConnected)panel.showPopover?.();if(onOpen)onOpen(panel);flyout.position()}else{panel.hidePopover?.();for(const inner of panel.querySelectorAll("details[open]"))inner.open=false}});details.addEventListener("keydown",event=>{if(event.key==="Escape"&&details.open){details.open=false;summary.focus();event.preventDefault();event.stopPropagation()}});return{details,panel}},buildGridColumns(){this.controlPanel(this.labels.columns,"cx-grid-columns",panel=>this.renderGridColumns(panel))},syncColumnControls(){},async renderGridColumns(panel){setText(panel,this.labels.loading);try{await loadGridFilterEditor();if(!this.destroyed&&panel.parentElement.open){this.renderGridColumns(panel);this.controlFlyouts.forEach(item=>item.position())}}
845
- catch(error){if(!this.destroyed){setText(panel,this.labels.gridLoadFailed);this.emit("error",{error})}}},showAllColumns(){for(const column of this.columns)column.hidden=false;this.buildHeader();this.syncColumnControls();this.render();this.noteViewPreferenceIntent?.();this.stateChanged()},resetColumns(){const state=this.getState();this.restorePreferenceIntent=this.viewPreferenceIntent;this.applyStatePresentation({...state,columns:this.readGridState({...state,columns:this.defaultState.columns}).columns});this.noteViewPreferenceIntent?.();this.stateChanged()},buildSavedViews(){const panel=this.controlPanel(this.labels.gridViews,"cx-grid-views").panel;const choice=this.savedChoice=h("select"),name=h("input");name.type="text";name.maxLength=100;choice.setAttribute("aria-label",this.labels.gridChooseView);panel.appendChild(choice);panel.appendChild(gridInputLabel(this.labels.gridViewName,name));const error=h("p","cx-grid-control-error");error.setAttribute("role","status");const run=action=>{try{Promise.resolve(action()).then(()=>setText(error,""),failure=>setText(error,failure.message))}
846
- catch(failure){setText(error,failure.message)}};panel.appendChild(gridButton(this.labels.gridApply,()=>run(()=>this.applySavedView(choice.value))));panel.appendChild(gridButton(this.labels.gridSaveView,()=>run(()=>this.saveView(name.value))));panel.appendChild(gridButton(this.labels.gridRemoveView,()=>run(()=>this.removeSavedView(choice.value))));panel.appendChild(error);this.syncSavedViews()},syncSavedViews(){if(!this.savedChoice)return;const selected=this.savedChoice.value;this.savedChoice.textContent="";this.savedChoice.appendChild(gridOption("",this.labels.gridChooseView));this.getSavedViews().forEach(name=>this.savedChoice.appendChild(gridOption(name,name)));if(this.savedViews.has(selected))this.savedChoice.value=selected}});Object.assign(DataGrid.prototype,{buildGridChrome(){const signal=this.lifetime.signal;this.noun=this.view.noun||this.labels.gridRows;if(this.searchInput&&this.view.noun){this.searchInput.placeholder=fill(this.labels.gridSearchNoun,{noun:this.noun});this.searchInput.setAttribute("aria-label",this.searchInput.placeholder)}
955
+ if(focused){const target=[...this.queryChips.children].find(button=>button.getAttribute("data-cx-condition")===key);(target||this.queryChips.lastElementChild||this.searchInput||this.grid).focus({preventScroll:true})}
956
+ this.gridPopover?.position()}});DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridFilters:"Filters",gridColumn:"Column",gridOperator:"Condition",gridValueType:"Raw value type",gridText:"Text",gridBoolean:"Boolean",gridRemoveFilter:"Remove {column} filter",gridApply:"Apply",gridClearFilters:"Clear all filters",gridFitAllColumns:"Fit all columns",gridViews:"Views",gridViewName:"View name",gridSaveView:"Save view",gridRemoveView:"Remove view",gridResetView:"Reset view",gridChooseView:"Choose a saved view",gridInvalidFilter:"Enter a valid filter value.",gridContains:"contains",gridStartsWith:"starts with",gridIn:"is any of",gridIsNull:"is missing",gridIsNotNull:"is present",gridFindColumn:"Find a column…",gridShowAllColumns:"Show all",gridResetColumns:"Reset columns",gridReorderColumn:"{column}: drag, or press Alt+Up or Alt+Down to move",gridPinnedLeft:"Pinned left",gridPinnedRight:"Pinned right"}));Object.assign(DataGrid.prototype,TOOLBAR_PANELS,{buildGridControls(){this.startMenus();this.buildGridFilters();if(this.tools.columns!==false)this.buildGridColumns();if(this.tools.views!==false){this.buildSavedViews();this.toolbar.querySelector(".cx-grid-views .cx-grid-panel").appendChild(gridButton(this.labels.gridResetView,()=>this.resetView().catch(()=>{})))}
957
+ this.buildExportControls()},buildGridColumns(){this.controlPanel(this.labels.columns,"cx-grid-columns",panel=>this.renderGridColumns(panel))},syncColumnControls(){},async renderGridColumns(panel){setText(panel,this.labels.loading);try{await loadGridFilterEditor();if(!this.destroyed&&panel.parentElement.open){this.renderGridColumns(panel);this.controlFlyouts.forEach(item=>item.position())}}
958
+ catch(error){if(!this.destroyed){setText(panel,this.labels.gridLoadFailed);this.emit("error",{error})}}},showAllColumns(){for(const column of this.columns)column.hidden=false;this.buildHeader();this.syncColumnControls();this.render();this.noteViewPreferenceIntent?.();this.stateChanged()},resetColumns(){const state=this.getState();this.restorePreferenceIntent=this.viewPreferenceIntent;this.applyStatePresentation({...state,columns:this.readGridState({...state,columns:this.defaultState.columns}).columns});this.noteViewPreferenceIntent?.();this.stateChanged()}});Object.assign(DataGrid.prototype,{buildGridChrome(){const signal=this.lifetime.signal;this.noun=this.view.noun||this.labels.gridRows;if(this.searchInput&&this.view.noun){this.searchInput.placeholder=fill(this.labels.gridSearchNoun,{noun:this.noun});this.searchInput.setAttribute("aria-label",this.searchInput.placeholder)}
847
959
  this.spacer=h("span","cx-spacer");this.selectionBar=h("div","cx-selbar");this.selectionBar.hidden=true;this.selectedCount=h("span");this.selectedCount.setAttribute("role","status");this.selectionBar.appendChild(this.selectedCount);const act=action=>Promise.resolve().then(action).catch(error=>this.emit("error",{error}));this.selectionBar.appendChild(gridDecorateButton(gridButton(this.labels.gridCopy,()=>act(()=>this.copy({scope:"selected"}))),"copy",true));if(typeof this.opts.onChart==="function")this.selectionBar.appendChild(gridDecorateButton(gridButton(this.labels.gridChart,()=>act(()=>
848
960
  this.runGridHook(signal=>this.opts.onChart({query:this.getQuery(),selection:this.getSelectionDescriptor?.()||
849
- {kind:"ids",ids:[...this.selected],revision:this.snapshot?.revision},signal}),this.dataLifetime.signal))),"chart",true));const clear=gridDecorateButton(gridButton("",()=>this.setSelection([])),"close",true);clear.classList.add("icon");clear.setAttribute("aria-label",this.labels.gridClearSelection);this.selectionBar.appendChild(clear);this.toolbarSeparator=h("span","cx-sep");const overflow=this.overflow=this.controlPanel(this.labels.gridMore,"cx-grid-overflow");const summary=overflow.details.firstChild;summary.replaceChildren(gridIcon("more"));summary.classList.add("icon");summary.setAttribute("aria-label",this.labels.gridMore);for(const action of this.opts.actions||[])overflow.panel.appendChild(this.gridAction(action,act));overflow.panel.append(this.toolbar.querySelector(".cx-grid-details"),this.toolbar.querySelector(".cx-grid-print")||
850
- Object.assign(gridButton(this.labels.gridPrint,()=>global.print()),{className:"cx-button cx-grid-print"}));(this.toolbar.querySelector(".cx-grid-export")||overflow.details).before(this.spacer,this.selectionBar,this.toolbarSeparator);if(this.tools.more===false){overflow.details.remove();this.overflow=null}
961
+ {kind:"ids",ids:[...this.selected],revision:this.snapshot?.revision},signal}),this.dataLifetime.signal))),"chart",true));const clear=gridDecorateButton(gridButton("",()=>this.setSelection([])),"close",true);clear.classList.add("icon");clear.setAttribute("aria-label",this.labels.gridClearSelection);this.selectionBar.appendChild(clear);this.toolbarSeparator=h("span","cx-sep");const overflow=this.overflow=this.controlPanel(this.labels.gridMore,"cx-grid-overflow");const summary=overflow.details.firstChild;summary.replaceChildren(gridIcon("more"));summary.classList.add("icon");summary.setAttribute("aria-label",this.labels.gridMore);for(const action of[...this.opts.actions||[],...pluginItems(this,"menuItems")])overflow.panel.appendChild(this.gridAction(action,act));overflow.panel.append(this.toolbar.querySelector(".cx-grid-details"),this.toolbar.querySelector(".cx-grid-print")||
962
+ Object.assign(gridButton(this.labels.gridPrint,()=>global.print()),{className:"cx-button cx-grid-print"}));(this.toolbar.querySelector(".cx-grid-export")||overflow.details).before(this.spacer,this.selectionBar,this.toolbarSeparator);for(const item of pluginItems(this,"toolbarItems"))this.spacer.before(this.gridAction(item,act));if(this.tools.more===false){overflow.details.remove();this.overflow=null}
851
963
  this.host.addEventListener("canopyx:select",()=>this.syncGridChrome(),{signal});this.syncGridChrome();signal.addEventListener("abort",()=>{this.headerSelectionFlyout?.dispose();this.headerMenusLifetime?.abort();this.closeGridPopover(false)},{once:true})},syncGridChrome(){if(!this.selectionBar)return;const count=this.querySelection?this.querySelection.matched===null?null:Math.max(0,this.querySelection.matched-this.selectionExcluded.size):this.selected.size;const selected=count===null||count>0;this.selectionBar.hidden=!selected;this.toolbarSeparator.hidden=!selected;this.host.classList.toggle("cx-has-selection",selected);gridCountLabel(this.selectedCount,count===null?this.labels.gridSelectionQueryUnknown:this.labels.gridSelectionCount,{count:numberFormat(this.lang).format(count),excluded:String(this.selectionExcluded?.size||0)});const filterSummary=this.toolbar.querySelector(".cx-grid-filters > summary"),active=gridFilterCount(this.acceptedQuery);if(filterSummary){filterSummary.classList.toggle("active",!!active);filterSummary.querySelector(".badge")?.remove();if(active){const badge=h("span","badge");setText(badge,String(active));filterSummary.appendChild(badge)}}
852
- for(const button of this.selectionBar.querySelectorAll("button"))button.disabled=!!this.pendingQuery||!this.acceptedQuery;this.syncGridStatus?.()},updateGridPhone(){const phone=this.host.clientWidth<=640;this.host.classList.toggle("cx-phone",phone);if(phone===this.phone)return;this.phone=phone;this.version++;this.scheduleRender();if(this.overflow)for(const selector of[".cx-grid-views",".cx-grid-export"]){const item=this.toolbar.querySelector(selector);if(!item)continue;item.open=false;if(phone)this.overflow.panel.appendChild(item);else this.toolbar.insertBefore(item,selector===".cx-grid-views"?this.slots?.afterViews||this.spacer:this.overflow.details)}
853
- this.syncPinnedShadows()},syncPinnedShadows(){if(!this.pinnedLeftWidth&&!this.pinnedRightWidth)return;this.host.classList.toggle("cx-scroll-left",this.grid.scrollLeft>0);this.host.classList.toggle("cx-scroll-right",this.grid.scrollLeft+this.grid.clientWidth<this.grid.scrollWidth-1)},gridAction(action,act){if(!action||typeof action.label!=="string"||typeof action.run!=="function")throw new Error("CanopyX: actions need a label and handler.");return gridButton(action.label,()=>act(()=>this.runGridHook(signal=>action.run({query:this.getQuery(),signal}),this.dataLifetime.signal)))}});function gridColumnFiltered(query,id){const has=group=>group&&(group.filters.some(item=>item.column===id)||group.groups.some(has));return!!query&&(query.filters.some(item=>item.column===id)||!!has(query.filterGroup))}
964
+ for(const button of this.selectionBar.querySelectorAll("button"))button.disabled=!!this.pendingQuery||!this.acceptedQuery;this.syncGridStatus?.()},updateGridPhone(){const phone=this.host.clientWidth<=640;this.host.classList.toggle("cx-phone",phone);if(phone===this.phone)return;this.phone=phone;this.version++;this.scheduleRender();this.placeMenus(phone);this.syncPinnedShadows()},syncPinnedShadows(){if(!this.pinnedLeftWidth&&!this.pinnedRightWidth)return;this.host.classList.toggle("cx-scroll-left",this.grid.scrollLeft>0);this.host.classList.toggle("cx-scroll-right",this.grid.scrollLeft+this.grid.clientWidth<this.grid.scrollWidth-1)},gridAction(action,act){if(!action||typeof action.label!=="string"||typeof action.run!=="function")throw new Error("CanopyX: actions need a label and handler.");return gridButton(action.label,()=>act(()=>this.runGridHook(signal=>action.run({query:this.getQuery(),signal,api:this.host.canopyx}),this.dataLifetime.signal)))}});function gridColumnFiltered(query,id){const has=group=>group&&(group.filters.some(item=>item.column===id)||group.groups.some(has));return!!query&&(query.filters.some(item=>item.column===id)||!!has(query.filterGroup))}
854
965
  function gridColumnProjection(query,id){const project=group=>{if(!group)return undefined;const filters=group.filters.filter(item=>item.column!==id),groups=group.groups.map(project);if(group.operator==="or"&&(filters.length!==group.filters.length||groups.some(child=>!child)))return undefined;const children=groups.filter(Boolean);return filters.length||children.length?{operator:group.operator,filters,groups:children}:undefined};return{...query,filters:(query.filters||[]).filter(item=>item.column!==id),filterGroup:project(query.filterGroup)}}
855
- Object.assign(DataGrid.prototype,{closeGridPopover(focus=true){const open=this.gridPopover;if(!open)return;this.gridPopover=null;open.controller.abort();open.panel.remove();open.anchor?.setAttribute("aria-expanded","false");if(focus){const anchor=open.anchor?.isConnected&&!open.anchor.hidden?open.anchor:this.headRow.querySelector('[data-cx-column-menu="'+CSS.escape(open.columnId||"")+'"]');(anchor||this.grid).focus({preventScroll:true})}},buildColumnMenus(){this.headerMenusLifetime?.abort();this.headerMenusLifetime=new AbortController();const signal=this.headerMenusLifetime.signal;for(const cell of this.headCells){const id=cell.dataset.cxColumn,column=this.columns.find(item=>item.id===id);const filter=gridDecorateButton(gridButton("",()=>this.openColumnFilter(id,filter)),"filter",true),menu=gridDecorateButton(gridButton("",()=>this.openColumnMenu(id,menu)),"more",true);filter.classList.add("cx-grid-header-filter","icon");menu.classList.add("cx-grid-column-menu","icon");filter.dataset.cxFilterColumn=id;menu.dataset.cxColumnMenu=id;filter.setAttribute("aria-label",this.labels.gridHeaderFilter?fill(this.labels.gridHeaderFilter,{column:column.title}):column.title+": "+this.labels.gridFilterColumn);menu.setAttribute("aria-label",fill(this.labels.gridColumnMenu,{column:column.title}));menu.setAttribute("aria-haspopup","menu");menu.setAttribute("aria-expanded","false");filter.dataset.cxFiltered=String(gridColumnFiltered(this.acceptedQuery,id));filter.hidden=filter.dataset.cxFiltered!=="true";menu.addEventListener("keydown",event=>{if(event.key==="ArrowDown"){event.preventDefault();this.openColumnMenu(id,menu)}},{signal});cell.insertBefore(filter,cell.querySelector(".cx-resize"));cell.insertBefore(menu,cell.querySelector(".cx-resize"))}
856
- if(this.gridPopover)this.gridPopover.position()}});const GRID_PIN_PREPARE=DataGrid.prototype.prepareState,GRID_PIN_WIDTHS=DataGrid.prototype.applyWidths,GRID_PIN_CELL=DataGrid.prototype.newCell;function gridPinGeometry(grid){const width=column=>column.renderWidth??column.width;const budget=Math.max(0,grid.grid.clientWidth-(grid.view.reporting?.minimumCenterWidth||128));for(const column of grid.columns)column.effectivePin="none";const left=grid.cols.filter(column=>column.pin==="left"),right=grid.cols.filter(column=>column.pin==="right").reverse();let used=0;const stopped=[false,false];for(let index=0;index<Math.max(left.length,right.length);index++)for(const[side,column]of[left[index],right[index]].entries())if(column&&!stopped[side]){if(used+width(column)>budget)stopped[side]=true;else{used+=width(column);column.effectivePin=column.pin}}
966
+ Object.assign(DataGrid.prototype,{closeGridPopover(focus=true){const open=this.gridPopover;if(!open)return;this.gridPopover=null;open.controller.abort();open.panel.remove();open.anchor?.setAttribute("aria-expanded","false");if(focus){const anchor=open.anchor?.isConnected&&!open.anchor.hidden?open.anchor:this.headRow.querySelector('[data-cx-column-menu="'+CSS.escape(open.columnId||"")+'"]');(anchor||this.grid).focus({preventScroll:true})}},buildColumnMenus(){this.headerMenusLifetime?.abort();this.headerMenusLifetime=new AbortController();const signal=this.headerMenusLifetime.signal;for(const cell of this.headCells){const id=cell.dataset.cxColumn,column=this.columns.find(item=>item.id===id);const filter=gridDecorateButton(gridButton("",()=>this.openColumnFilter(id,filter)),"filter",true),menu=gridDecorateButton(gridButton("",()=>this.openColumnMenu(id,menu)),"more",true);filter.classList.add("cx-grid-header-filter","icon");menu.classList.add("cx-grid-column-menu","icon");filter.dataset.cxFilterColumn=id;menu.dataset.cxColumnMenu=id;filter.setAttribute("aria-label",this.labels.gridHeaderFilter?fill(this.labels.gridHeaderFilter,{column:column.title}):column.title+": "+this.labels.gridFilterColumn);menu.setAttribute("aria-label",fill(this.labels.gridColumnMenu,{column:column.title}));menu.setAttribute("aria-haspopup","menu");menu.setAttribute("aria-expanded","false");filter.dataset.cxFiltered=String(gridColumnFiltered(this.acceptedQuery,id));filter.hidden=filter.dataset.cxFiltered!=="true";menu.addEventListener("keydown",event=>{if(event.key==="ArrowDown"){event.preventDefault();this.openColumnMenu(id,menu)}},{signal});cell.insertBefore(filter,cell.querySelector(".cx-resize"));if(this.tools.columnMenus!==false)cell.insertBefore(menu,cell.querySelector(".cx-resize"))}
967
+ if(this.gridPopover)this.gridPopover.position()}});const GRID_PIN_PREPARE=DataGrid.prototype.prepareState,GRID_PIN_WIDTHS=DataGrid.prototype.applyWidths,GRID_PIN_CELL=DataGrid.prototype.newCell;function gridPinGeometry(grid){const width=column=>column.renderWidth??column.width;const budget=Math.max(0,grid.grid.clientWidth-(grid.view.reporting?.minimumCenterWidth||128));for(const column of grid.columns)column.effectivePin="none";const pinned=grid.cols.filter(column=>column.pin==="left"),identity=grid.layoutMode==="scroll"&&grid.cols[pinned.length];const left=identity?.identity&&identity.pin==="none"?[...pinned,identity]:pinned,right=grid.cols.filter(column=>column.pin==="right").reverse();const pinOf=column=>left.includes(column)?"left":column.pin;let used=0;const stopped=[false,false];for(let index=0;index<Math.max(left.length,right.length);index++)for(const[side,column]of[left[index],right[index]].entries())if(column&&!stopped[side]){if(used+width(column)>budget)stopped[side]=true;else{used+=width(column);column.effectivePin=pinOf(column)}}
857
968
  let start=0,end=0;for(const column of grid.cols){column.pinOffset=column.effectivePin==="left"?start:0;if(column.effectivePin==="left")start+=width(column)}
858
969
  for(const column of[...grid.cols].reverse())if(column.effectivePin==="right"){column.pinOffset=end;end+=width(column)}
859
970
  grid.pinnedLeftWidth=start;grid.pinnedRightWidth=end;grid.host.classList.toggle("cx-pins",used>0);grid.cols.forEach((column,index)=>grid.applyColumnPin(grid.headCells[index],column));for(const row of[...grid.rendered.values(),...grid.pool])if(row._kind==="node")grid.cols.forEach((column,index)=>{if(row.children[index])grid.applyColumnPin(row.children[index],column)})}
860
- DataGrid.prototype.prepareState=function(){gridFitPrepare(this);return GRID_PIN_PREPARE.call(this)};DataGrid.prototype.applyWidths=function(widths){GRID_PIN_WIDTHS.call(this,widths);if(!this.view.reporting)gridPinGeometry(this);this.syncPinnedShadows()};DataGrid.prototype.applyColumnPin=function(cell,column){const pin=column.effectivePin||"none";if(pin==="none")cell.removeAttribute("data-cx-pin");else cell.dataset.cxPin=pin;cell.style.left=pin==="left"?column.pinOffset+"px":"";cell.style.right=pin==="right"?column.pinOffset+"px":"";const side=pin==="left"?this.cols.filter(item=>item.effectivePin==="left").at(-1):this.cols.find(item=>item.effectivePin==="right");cell.classList.toggle("cx-pin-edge",side===column)};DataGrid.prototype.newCell=function(index,width,kind){const cell=GRID_PIN_CELL.call(this,index,width,kind);if(kind&&this.cols[index])this.applyColumnPin(cell,this.cols[index]);return cell};DataGrid.prototype.setColumnPinned=function(id,pin){const column=this.columns.find(item=>item.id===id);if(!column||!["none","left","right"].includes(pin)||this.destroyed)throw new Error("CanopyX: invalid column pin.");column.pin=pin;this.columns=["left","none","right"].flatMap(side=>this.columns.filter(item=>(item.pin||"none")===side));this.buildHeader();this.render();this.syncColumnControls();this.stateChanged();this.emit("columnPin",{column:id,pin})};const GRID_PIN_STATE_COLUMN=gridStateColumn,GRID_PIN_STATE_COLUMNS=gridStateColumns;gridStateColumn=column=>({...GRID_PIN_STATE_COLUMN(column),pin:column.pin||"none"});gridStateColumns=(grid,input)=>{const result=GRID_PIN_STATE_COLUMNS(grid,input),sources=new Map(input.map(column=>[column.id,column]));for(const saved of result){const pin=sources.get(saved.id)?.pin??saved.pin??"none";if(!["none","left","right"].includes(pin))throw new Error("CanopyX: invalid saved pin.");saved.pin=pin}return result};const FILTER_EDITOR_VERSION="34d1704269a2eb44",FILTER_EDITOR_SCRIPT="canopyx-filter-editor.bdc47d43672fd039.js",FILTER_EDITOR_STYLE="canopyx-filter-editor.fdae0d2581fbc259.css";const FILTER_RUNTIME_URL=typeof document!=="undefined"&&document.currentScript?.src;let filterEditorReady;function gridFilterEditorContext(){return{DataGrid,h,setText,gridButton,gridInputLabel,gridOption,gridScalar,gridQuery,gridQueryStart,GRID_FILTER_LABELS,gridResultShape,gridQueryRows,gridOrder,gridColumnProjection,gridCellRenderer,cellValue,cellText,foldText,reachable,gridMatchesGroup,gridDecorateButton,gridIcon,numberFormat,fill,GRID_REPORTING,reportingRangeRows:typeof reportingRangeRows==="function"?reportingRangeRows:null}}
861
- function loadGridFilterEditor(){if(filterEditorReady)return filterEditorReady;filterEditorReady=Promise.resolve().then(async()=>{if(global.CanopyXFilterEditor?.version!==FILTER_EDITOR_VERSION){const payload=document.querySelector('script[type="application/x-canopyx-filter-editor"][data-cx-version="'+FILTER_EDITOR_VERSION+'"]');const script=h("script"),style=h("style"),embeddedStyle=document.querySelector('script[type="application/x-canopyx-filter-style"][data-cx-version="'+FILTER_EDITOR_VERSION+'"]');if(payload){if(embeddedStyle){setText(style,embeddedStyle.textContent);if(payload.nonce)style.nonce=payload.nonce;document.head.appendChild(style)}
862
- setText(script,payload.textContent);if(payload.nonce)script.nonce=payload.nonce;document.head.appendChild(script);script.remove()}
863
- else{const runtime=FILTER_RUNTIME_URL||global.CanopyXFilterEditorBase||document.baseURI,hashed=FILTER_RUNTIME_URL&&/\.[a-f\d]{16}\.js(?:[?#]|$)/.test(FILTER_RUNTIME_URL);
864
- const link=h("link");link.rel="stylesheet";link.href=new URL(hashed?FILTER_EDITOR_STYLE:"canopyx-filter-editor.css",runtime).href;script.src=new URL(hashed?FILTER_EDITOR_SCRIPT:"canopyx-filter-editor.js",runtime).href;await Promise.all([new Promise((resolve,reject)=>{link.onload=resolve;link.onerror=()=>{link.remove();reject(new Error("CanopyX: filter editor stylesheet unavailable."))};document.head.appendChild(link)}),new Promise((resolve,reject)=>{script.onload=resolve;script.onerror=()=>{script.remove();reject(new Error("CanopyX: filter editor script unavailable."))};document.head.appendChild(script)})]);script.remove()}}
865
- if(global.CanopyXFilterEditor?.version!==FILTER_EDITOR_VERSION)throw new Error("CanopyX: filter editor and runtime must come from the same build.");global.CanopyXFilterEditor.install(gridFilterEditorContext())}).catch(error=>{filterEditorReady=null;throw error});return filterEditorReady}
866
- Object.assign(DataGrid.prototype,{buildGridFilters(){this.reportingFacetStops=new Set();this.reportingFacetSequence=0;if(this.tools.filters===false)return;this.reportingFilters=this.controlPanel(this.labels.gridFilters,"cx-grid-filters",panel=>this.renderGridFilters(panel))},async renderGridFilters(panel){const epoch=this.queryEpoch;setText(panel,this.labels.loading);try{await loadGridFilterEditor();if(epoch!==this.queryEpoch){panel.parentElement.open=false;return}if(!this.destroyed&&panel.isConnected&&panel.parentElement.open){this.renderGridFilters(panel);this.controlFlyouts.forEach(item=>item.position())}}
971
+ DataGrid.prototype.prepareState=function(){gridFitPrepare(this);return GRID_PIN_PREPARE.call(this)};DataGrid.prototype.applyWidths=function(widths){GRID_PIN_WIDTHS.call(this,widths);if(!this.view.reporting)gridPinGeometry(this);this.syncPinnedShadows()};DataGrid.prototype.applyColumnPin=function(cell,column){const pin=column.effectivePin||"none";if(pin==="none")cell.removeAttribute("data-cx-pin");else cell.dataset.cxPin=pin;cell.style.left=pin==="left"?column.pinOffset+"px":"";cell.style.right=pin==="right"?column.pinOffset+"px":"";const side=pin==="left"?this.cols.filter(item=>item.effectivePin==="left").at(-1):this.cols.find(item=>item.effectivePin==="right");cell.classList.toggle("cx-pin-edge",side===column)};DataGrid.prototype.newCell=function(index,width,kind){const cell=GRID_PIN_CELL.call(this,index,width,kind);if(kind&&this.cols[index])this.applyColumnPin(cell,this.cols[index]);return cell};DataGrid.prototype.setColumnPinned=function(id,pin){const column=this.columns.find(item=>item.id===id);if(!column||!["none","left","right"].includes(pin)||this.destroyed)throw new Error("CanopyX: invalid column pin.");column.pin=pin;this.columns=["left","none","right"].flatMap(side=>this.columns.filter(item=>(item.pin||"none")===side));this.buildHeader();this.render();this.syncColumnControls();this.stateChanged();this.emit("columnPin",{column:id,pin})};const GRID_PIN_STATE_COLUMN=gridStateColumn,GRID_PIN_STATE_COLUMNS=gridStateColumns;gridStateColumn=column=>({...GRID_PIN_STATE_COLUMN(column),pin:column.pin||"none"});gridStateColumns=(grid,input)=>{const result=GRID_PIN_STATE_COLUMNS(grid,input),sources=new Map(input.map(column=>[column.id,column]));for(const saved of result){const pin=sources.get(saved.id)?.pin??saved.pin??"none";if(!["none","left","right"].includes(pin))throw new Error("CanopyX: invalid saved pin.");saved.pin=pin}return result};const FILTER_EDITOR=definePart(DataGrid,"filter-editor","filter editor",()=>({DataGrid,h,setText,gridButton,gridInputLabel,gridOption,gridScalar,gridDateInputMoment,gridQuery,gridQueryStart,GRID_FILTER_LABELS,gridResultShape,gridQueryRows,gridOrder,gridColumnProjection,gridCellRenderer,cellValue,cellText,foldText,reachable,gridMatchesGroup,gridDecorateButton,gridIcon,numberFormat,numberText,fill,announceMenu,applyTone,toneOf,badgeElement,isCellObject,RECORD_IMPL,GRID_REPORTING,reportingRangeRows:typeof reportingRangeRows==="function"?reportingRangeRows:null}));function loadGridFilterEditor(){return loadPart(FILTER_EDITOR)}
972
+ DataGrid.prototype.requirePart=requirePart;Object.assign(DataGrid.prototype,{buildGridFilters(){this.reportingFacetStops=new Set();this.reportingFacetSequence=0;if(this.tools.filters===false||!this.columns.some(column=>column.filterable!==false))return;this.reportingFilters=this.controlPanel(this.labels.gridFilters,"cx-grid-filters",panel=>this.renderGridFilters(panel))},async renderGridFilters(panel){const epoch=this.queryEpoch;setText(panel,this.labels.loading);try{await loadGridFilterEditor();if(epoch!==this.queryEpoch){panel.parentElement.open=false;return}if(!this.destroyed&&panel.isConnected&&panel.parentElement.open){this.renderGridFilters(panel);this.controlFlyouts.forEach(item=>item.position())}}
867
973
  catch(error){if(!this.destroyed&&panel.isConnected){setText(panel,this.labels.gridLoadFailed);panel.appendChild(gridButton(this.labels.gridRetry,()=>this.renderGridFilters(panel)));this.emit("error",{error})}}},async showReportingText(...args){const epoch=this.queryEpoch;try{await loadGridFilterEditor();if(!this.destroyed&&epoch===this.queryEpoch)this.showReportingText(...args)}catch(error){this.emit("error",{error})}},});for(const method of["openColumnMenu","openColumnFilter"])DataGrid.prototype[method]=async function(id,anchor){try{await loadGridFilterEditor();if(!this.destroyed)this[method](id,anchor?.isConnected?anchor:this.headRow.querySelector('[data-cx-column-menu="'+CSS.escape(id)+'"]'))}
868
- catch(error){this.setNotice(this.labels.gridLoadFailed,true);this.emit("error",{error})}};const gridExportSignal=(signal,options)=>AbortSignal.any([signal,options?.signal].filter(Boolean));function gridExportField(value,delimiter){if(value==null)return "";let text=String(value);if(typeof value!=="number"&&/^[\s\uFEFF]*[=+\-@]/.test(text)) text = "'" + text;
869
- return text.includes(delimiter)||/["\r\n]/.test(text) ? '"' + text.replace(/"/g, '""') + '"' : text;
870
- }
871
- function gridOperationActive(grid,signal){if(grid.destroyed||signal?.aborted)throw new DOMException("Grid cancelled.","AbortError")}
872
- Object.assign(DataGrid.prototype,{prepareExport(options){if(!this.view.reporting&&["fileName","locale","timeZone","onProgress"].some(key=>options?.[key]!==undefined))
873
- throw new Error("CanopyX: export metadata requires reporting.");if(this.destroyed||!this.acceptedQuery)throw new Error("CanopyX: export needs an accepted query.");if(!options||!["filtered","selected","all"].includes(options.scope))throw new Error("CanopyX: explicit export scope required.");const{scope,values,signal}=options,filtered=scope==="filtered";const selection=scope==="selected"?[...this.selected]:[];const columnIds=options.columns===undefined?this.visibleColumns().map(column=>column.id):options.columns;const columns=Array.isArray(columnIds)?columnIds.map(id=>this.columns.find(column=>column.id===id)):[];if(!Array.isArray(columnIds)||!columnIds.length||new Set(columnIds).size!==columnIds.length||
874
- columns.some(column=>!column))throw new Error("CanopyX: invalid export columns.");if(values!==undefined&&!["raw","display"].includes(values))throw new Error("CanopyX: export values must be raw/display.");if(signal!==undefined&&!(signal instanceof AbortSignal))throw new Error("CanopyX: export requires AbortSignal.");const query=gridQuery(this.view,{...this.acceptedQuery,offset:0,cursor:undefined,limit:this.pageSize,search:filtered?this.acceptedQuery.search:"",filterGroup:filtered?this.acceptedQuery.filterGroup:undefined,filters:filtered?this.acceptedQuery.filters:[]});const{revision,total,matched}=this.snapshot;const request=gridFreeze({scope,query,revision,total,matched,recordCount:scope==="selected"?selection.length:
875
- filtered?matched:total,selection,columns:columns.map(({id,title,kind})=>({id,title,kind})),presentation:GRID_REPORTING.exportRecord?"semantic":"text",values:values||"display",timeZone:this.local?"local":"utc"});return Object.freeze({request,rows:options=>this.exportRows(request,gridExportSignal(signal,options))})},async*exportRows(request,signal){const{scope}=request;const fmt=formatting(request.locale||this.lang,request.timeZone==="local",this.labels);gridOperationActive(this,signal);const remaining=new Set(request.selection),seen=new Set();if(scope==="selected"&&!remaining.size)return;const expected={revision:request.revision,total:request.total,matched:scope==="filtered"?request.matched:this.cursor?undefined:request.total};const tokens=new Set();let cursor,offset=0;while(this.cursor||offset<expected.matched){gridOperationActive(this,signal);const query=gridQuery(this.view,{...request.query,offset:this.cursor?undefined:offset,cursor});const page=(this.cursor?validateGridCursorPage:validateGridPage)(await this.requestPage(query,signal),this.view,query,expected);if(this.cursor&&(page.offset!==offset||scope!=="filtered"&&page.total!==null&&
876
- (page.matched!==null&&page.matched!==page.total||page.nextCursor===undefined&&page.offset+page.items.length!==page.total)))
877
- throw new Error("CanopyX: incomplete cursor export.");expected.matched=page.matched;for(const row of page.items){gridOperationActive(this,signal);if(seen.has(row.id))throw new Error("CanopyX: repeated export row ID.");seen.add(row.id);if(scope!=="selected"||remaining.delete(row.id)){yield GRID_REPORTING.exportRecord?GRID_REPORTING.exportRecord(this,request,row):gridFreeze({id:row.id,cells:Object.fromEntries(request.columns.map(column=>[column.id,{value:cellValue(row.cells[column.id])??null,text:cellText(row.cells[column.id],column,fmt)}]))})}}
878
- offset +=this.cursor?page.items.length:request.query.limit;if(this.cursor){cursor=page.nextCursor;if(cursor===undefined)break;if(tokens.has(cursor))throw new Error("CanopyX: cursor export repeated a token.");tokens.add(cursor)}}
879
- gridOperationActive(this,signal);if(remaining.size)throw new Error("CanopyX: selected IDs absent from revision.")},async exportText(exported,maxRows=50000,delimiter,signal){if(!Number.isInteger(maxRows)||maxRows<1||maxRows>1000000)throw new Error("CanopyX: export row limit is 1 to 1000000.");const request=exported.request;if(request.recordCount>maxRows)throw new Error("CanopyX: client row limit exceeded.");const lines=[request.columns.map(column=>gridExportField(column.title,delimiter)).join(delimiter)];let count=0;for await(const row of exported.rows()){if(++count>maxRows)throw new Error("CanopyX: client row limit exceeded.");lines.push(request.columns.map(column=>gridExportField(request.values==="raw"?row.cells[column.id]?.value:
880
- row.cells[column.id]?.text,delimiter)).join(delimiter))}
881
- gridOperationActive(this,signal);return lines.join("\r\n")+ "\r\n"},toCsv(options){return this.exportText(this.prepareExport(options),options.maxRows,",",options.signal)},exportToHost(format,options){const capture=this.prepareExport(options);if(typeof format!=="string"||!format.trim()||typeof this.opts.onExport!=="function")throw new Error("CanopyX: invalid host export.");return this.runGridHook(signal=>this.opts.onExport({format,request:capture.request,capture,signal}),options.signal)}});Object.assign(DataGrid.prototype,{scrollToRow(index){if(this.destroyed||!this.acceptedQuery||this.pendingQuery||!Number.isInteger(index)||
974
+ catch(error){this.setNotice(this.labels.gridLoadFailed,true);this.emit("error",{error})}};function gridOperationActive(grid,signal){if(grid.destroyed||signal?.aborted)throw new DOMException("Grid cancelled.","AbortError")}
975
+ const exportPartReady=()=>typeof loadExportPart==="function"?loadExportPart():Promise.resolve();function exportAs(grid,format,options={}){const{onProgress,timeoutMs,download,...capture}=options;let provider,resolutionError;try{provider=EXPORT_IMPL.formatsOf(grid).find(item=>item.id===format)}
976
+ catch(error){resolutionError=error}
977
+ const prepared=grid.calIcsFormatWriter&&provider?.write===grid.calIcsFormatWriter?grid.calPrepareIcsExport(capture):grid.prepareExport(capture);return exportPartReady().then(()=>{if(resolutionError)throw resolutionError;return EXPORT_IMPL.run(grid,format,options,prepared,provider)})}
978
+ const gridExportSignal=(signal,options)=>AbortSignal.any([signal,options?.signal].filter(Boolean));Object.assign(DataGrid.prototype,{defaultExportColumns(){return this.visibleColumns().filter(column=>column.exportable!==false)},prepareExport(options){if(!this.view.reporting&&["fileName","locale","timeZone","onProgress"].some(key=>options?.[key]!==undefined))
979
+ throw new Error("CanopyX: export metadata requires reporting.");if(this.destroyed||!this.acceptedQuery)throw new Error("CanopyX: export needs an accepted query.");if(!options||!["filtered","selected","all"].includes(options.scope))throw new Error("CanopyX: explicit export scope required.");const{scope,values,signal}=options,filtered=scope==="filtered";const selection=scope==="selected"?[...this.selected]:[];const columnIds=options.columns===undefined?this.defaultExportColumns().map(column=>column.id):options.columns;if(options.columns===undefined&&!columnIds.length)throw new Error(this.labels.gridNoExportColumns);const columns=Array.isArray(columnIds)?columnIds.map(id=>this.columns.find(column=>column.id===id)):[];if(!Array.isArray(columnIds)||!columnIds.length||new Set(columnIds).size!==columnIds.length||
980
+ columns.some(column=>!column||column.exportable===false))throw new Error("CanopyX: invalid or unsupported export columns.");if(values!==undefined&&!["raw","display"].includes(values))throw new Error("CanopyX: export values must be raw/display.");if(signal!==undefined&&!(signal instanceof AbortSignal))throw new Error("CanopyX: export requires AbortSignal.");const query=gridQuery(this.view,{...this.acceptedQuery,offset:0,cursor:undefined,limit:this.pageSize,search:filtered?this.acceptedQuery.search:"",filterGroup:filtered?this.acceptedQuery.filterGroup:undefined,filters:filtered?this.acceptedQuery.filters:[]});const{revision,total,matched}=this.snapshot;const request=gridFreeze({scope,query,revision,total,matched,recordCount:scope==="selected"?selection.length:
981
+ filtered?matched:total,selection,columns:columns.map(({id,title,kind,format})=>({id,title,kind,...(format?{format:{...format}}:{})})),presentation:GRID_REPORTING.exportRecord?"semantic":"text",values:values||"display",timeZone:this.local?"local":"utc",...GRID_REPORTING.exportRequest?.(this,options)});return Object.freeze({request,rows:options=>this.exportRows(request,gridExportSignal(signal,options))})}});DataGrid.drawerHooks=[];DataGrid.activeDrawers=new Map();DataGrid.applyDrawerHooks=(grid,dialog)=>{const applied=dialog._cxDrawerHooks||=new Set();for(const entry of DataGrid.drawerHooks)if(!applied.has(entry)){applied.add(entry);try{entry.hook.call(grid,dialog)}catch(error){grid.emit("error",{error})}}};DataGrid.refreshDrawerHooks=(grid,dialog)=>{if(dialog._cxHooksFrame)return;dialog._cxHooksFrame=requestAnimationFrame(()=>{dialog._cxHooksFrame=0;if(grid.destroyed||!dialog.isConnected)return;DataGrid.applyDrawerHooks(grid,dialog);if(dialog._cxRecord)dialog.dispatchEvent(new CustomEvent("cx-drawer-paint",{detail:{id:dialog._cxRecord.id,index:dialog._cxIndex??null}}))})};DataGrid.drawerHook=(order,hook)=>{DataGrid.drawerHooks.push({order,hook});DataGrid.drawerHooks.sort((a,b)=>a.order-b.order);for(const[dialog,grid]of DataGrid.activeDrawers)DataGrid.refreshDrawerHooks(grid,dialog)};Object.assign(DataGrid.prototype,{focusRing:focusDrawerTitle,scrollToRow(index){if(this.destroyed||!this.acceptedQuery||this.pendingQuery||!Number.isInteger(index)||
882
982
  index<(this.cursor?this.pageOffset:0)||index>=this.rowCount())
883
- throw new Error("CanopyX: invalid accepted row position.");return this.focusResult(index,false)},async runGridHook(run,signal){const controller=new AbortController();let rejectHook;const cancelled=new Promise((resolve,reject)=>{rejectHook=reject});const abort=()=>{controller.abort();rejectHook(new DOMException("Grid cancelled.","AbortError"))};this.lifetime.signal.addEventListener("abort",abort,{once:true});signal?.addEventListener("abort",abort,{once:true});const timer=setTimeout(()=>{controller.abort();rejectHook(new Error("CanopyX: operation timed out."))},this.requestTimeoutMs);try{if(this.destroyed||signal?.aborted)abort();return await Promise.race([Promise.resolve().then(()=>{gridOperationActive(this,controller.signal);return run(controller.signal)}),cancelled])}finally{clearTimeout(timer);controller.abort();this.lifetime.signal.removeEventListener("abort",abort);signal?.removeEventListener("abort",abort)}},async copy(options){const exported=this.prepareExport(options);const text=await this.exportText(exported,options.maxRows===undefined?10000:options.maxRows,"\t",options.signal);await this.writeClipboard(text,options.signal);return text},writeClipboard(text,signal){return this.runGridHook(()=>{const clipboard=global.navigator.clipboard;if(typeof clipboard?.writeText!=="function")throw new Error("CanopyX: clipboard unavailable.");return clipboard.writeText(text)},signal)},copyFocused(row,cell){if(this.selected.size)return this.copy({scope:"selected"});const columns=this.visibleColumns(),index=cell?this.rowCells(this.rendered.get(row)).indexOf(cell):-1;const text=index>=0?gridExportField(cellText(row.h.cells[columns[index].id],columns[index],this.fmt),"\t"):
884
- columns.map(column=>gridExportField(cellText(row.h.cells[column.id],column,this.fmt),"\t")).join("\t");return this.writeClipboard(text)},openDetails(id){requireIdentifier(id,"detail row ID");if(this.destroyed||!this.acceptedQuery||this.pendingQuery)
885
- throw new Error("CanopyX: details require an accepted query.");const row=[...this.rowCache.values()].find(item=>item.key===id);if(!row)throw new Error("CanopyX: details require a loaded record.");if(typeof this.opts.onDetails!=="function"){const epoch=this.queryEpoch,returnFocus=document.activeElement;return loadGridFilterEditor().then(()=>{if(!this.destroyed&&!this.pendingQuery&&epoch===this.queryEpoch)this.openGridDrawer(row.h,row._ri,returnFocus)})}
983
+ throw new Error("CanopyX: invalid accepted row position.");return this.focusResult(index,false)},async runGridHook(run,signal,timeout=this.requestTimeoutMs){const controller=new AbortController();let rejectHook;const cancelled=new Promise((resolve,reject)=>{rejectHook=reject});const abort=()=>{controller.abort();rejectHook(new DOMException("Grid cancelled.","AbortError"))};this.lifetime.signal.addEventListener("abort",abort,{once:true});signal?.addEventListener("abort",abort,{once:true});const timer=setTimeout(()=>{controller.abort();rejectHook(new Error("CanopyX: operation timed out."))},timeout);try{if(this.destroyed||signal?.aborted)abort();return await Promise.race([Promise.resolve().then(()=>{gridOperationActive(this,controller.signal);return run(controller.signal)}),cancelled])}finally{clearTimeout(timer);controller.abort();this.lifetime.signal.removeEventListener("abort",abort);signal?.removeEventListener("abort",abort)}},openDetails(id){requireIdentifier(id,"detail row ID");if(this.destroyed||!this.acceptedQuery||this.pendingQuery)
984
+ throw new Error("CanopyX: details require an accepted query.");const row=[...this.rowCache.values()].find(item=>item.key===id);if(!row)throw new Error("CanopyX: details require a loaded record.");if(typeof this.opts.onDetails!=="function"){const epoch=this.queryEpoch,returnFocus=document.activeElement;return Promise.all([loadGridFilterEditor(),this.recordNeeded(row.h)&&loadRecordPanel()]).then(()=>{if(!this.destroyed&&!this.pendingQuery&&epoch===this.queryEpoch)this.openGridDrawer(row.h,row._ri,returnFocus)})}
886
985
  if(this.detailController)this.detailController.abort();const controller=this.detailController=new AbortController(),dataSignal=this.dataLifetime.signal;const abort=()=>controller.abort();dataSignal.addEventListener("abort",abort,{once:true});const record=gridFreeze(JSON.parse(JSON.stringify(row.h))),query=this.acceptedQuery,returnFocus=document.activeElement;return this.runGridHook(signal=>this.opts.onDetails({row:record,query,signal,returnFocus}),controller.signal)
887
- .finally(()=>{dataSignal.removeEventListener("abort",abort);if(this.detailController===controller)this.detailController=null})}});DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridExport:"Export",gridFilteredResults:"Filtered results",gridSelectedRows:"Selected rows",gridAllResults:"All results",gridDownloadCsv:"Download CSV",gridCopy:"Copy",gridDetails:"Record details"}));Object.assign(DataGrid.prototype,{buildExportControls(){this.toolbar.appendChild(Object.assign(gridButton(this.labels.gridDetails,()=>{try{const row=this.focused||[...this.rowCache.values()].find(item=>this.selected.has(item.key));if(!row||row.status)throw new Error("CanopyX: focus a record for details.");this.openDetails(row.key).catch(error=>this.emit("error",{error}))}catch(error){this.emit("error",{error})}}),{className:"cx-button cx-grid-details"}));if(this.tools.export===false)return;const panel=this.controlPanel(this.labels.gridExport,"cx-grid-export").panel,scope=h("select");scope.setAttribute("aria-label",this.labels.gridExport);for(const[value,label]of[["filtered","gridFilteredResults"],["selected","gridSelectedRows"],["all","gridAllResults"]]){scope.appendChild(gridOption(value,this.labels[label]))}
888
- panel.appendChild(scope);const status=h("p","cx-grid-control-error");status.setAttribute("role","status");const run=action=>{buttons.forEach(button=>button.disabled=true);setText(status,this.labels.loading);Promise.resolve().then(action).then(()=>setText(status,""),error=>{setText(status,error.message);this.emit("error",{error})}).finally(()=>{if(!this.destroyed)buttons.forEach(button=>button.disabled=false)})};const csv=gridButton(this.labels.gridDownloadCsv,()=>run(async()=>{const text=await this.toCsv({scope:scope.value}),url=URL.createObjectURL(new Blob([text],{type:"text/csv;charset=utf-8"}));try{if(this.destroyed)return;const link=h("a");link.href=url;link.download=this.view.id.replace(/[^a-z0-9_-]/gi, "_") + ".csv";
889
- document.body.appendChild(link);link.click();link.remove()}finally{setTimeout(()=>URL.revokeObjectURL(url),1000)}}));const copy=gridButton(this.labels.gridCopy,()=>run(()=>this.copy({scope:scope.value})));const buttons=[csv,copy];panel.append(csv,copy,status)}});DataGrid.prototype.paintHiddenFields=function(slot,row,column){let button=slot.hiddenFields;const count=this.phone&&column===this.cols[0]&&this.columns.filter(item=>item.hidden||item.responsiveHidden||item.fitHidden).length;if(!count){button?.remove();slot.hiddenFields=null;return}
986
+ .finally(()=>{dataSignal.removeEventListener("abort",abort);if(this.detailController===controller)this.detailController=null})}});DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridExport:"Export",gridFilteredResults:"Filtered results",gridSelectedRows:"Selected rows",gridAllResults:"All results",gridVisibleRows:"Visible rows",gridAllRows:"All rows",gridDownloadCsv:"Download CSV",gridCopy:"Copy",gridDetails:"Record details",gridExportProgress:"{completed} of {total} records",gridExportReading:"{completed} records",gridExportCancelled:"Export cancelled",gridCancelExport:"Cancel export",gridNoExportColumns:"No visible columns allow export."}));const GRID_EXPORT_PART=definePart(DataGrid,"export","export part",()=>({...FILTER_EDITOR.context(),EXPORT_IMPL,gridOperationActive,formatting,validateGridCursorPage,validateGridPage,gridFreeze,isCellObject,exportTimeout,exportFormat,EXPORT_FORMATS,gridExportField}));function loadExportPart(){return loadPart(GRID_EXPORT_PART)}
987
+ for(const name of["toCsv","copy","exportToHost"])DataGrid.prototype[name]=function(...args){const capture=this.prepareExport(args.at(-1));return loadExportPart().then(()=>EXPORT_IMPL[name].call(this,capture,...args))};for(const name of["exportText","copyFocused"])DataGrid.prototype[name]=function(...args){return loadExportPart().then(()=>EXPORT_IMPL[name].apply(this,args))};Object.assign(DataGrid.prototype,{async*exportRows(...args){await loadExportPart();yield*EXPORT_IMPL.exportRows.apply(this,args)},exportAs(format,options){return exportAs(this,format,options)},buildExportControls(){this.toolbar.appendChild(Object.assign(gridButton(this.labels.gridDetails,()=>{try{const row=this.focused||[...this.rowCache.values()].find(item=>this.selected.has(item.key));if(!row||row.status)throw new Error("CanopyX: focus a record for details.");this.openDetails(row.key).catch(error=>this.emit("error",{error}))}catch(error){this.emit("error",{error})}}),{className:"cx-button cx-grid-details"}));if(this.tools.export!==false)this.exportControl=this.controlPanel(this.labels.gridExport,"cx-grid-export",panel=>this.renderGridExport(panel))},async renderGridExport(panel){if(panel.firstChild)return;setText(panel,this.labels.loading);try{await loadExportPart();if(!this.destroyed&&panel.isConnected){this.renderGridExport(panel);this.controlFlyouts.forEach(item=>item.position())}}
988
+ catch(error){if(!this.destroyed&&panel.isConnected){setText(panel,this.labels.gridLoadFailed);panel.appendChild(gridButton(this.labels.gridRetry,()=>this.renderGridExport(panel,setText(panel,""))));this.emit("error",{error})}}}});DataGrid.prototype.hiddenFieldCount=function(){return this.columns.filter(item=>item.hidden||item.responsiveHidden||item.fitHidden).length};DataGrid.prototype.paintHiddenFields=function(slot,row,column){let button=slot.hiddenFields;const count=this.phone&&column===this.cols[0]&&this.hiddenFieldCount();if(!count){button?.remove();slot.hiddenFields=null;return}
890
989
  if(!button){button=gridButton("",()=>Promise.resolve().then(()=>this.openDetails(button.dataset.cxRecord)).catch(error=>this.emit("error",{error})));button.classList.add("cx-hidden-fields");button.tabIndex=-1}
891
- if(button.parentNode!==slot.cell)slot.cell.appendChild(button);button.dataset.cxRecord=row.id;button.setAttribute("aria-label",this.labels.gridDetails);setText(button,"+"+count);slot.hiddenFields=button};function createDataGrid(host,options){if(!host||!options||!options.view)throw new Error("CanopyX: createDataGrid needs a host element and a grid document.");const existing=activeGrids.get(host);if(existing)return existing;const grid=new DataGrid(host,options);const api={element:host,view:grid.view,ready:grid.ready,getTimeZone:()=>grid.local?"local":"utc",getSelection:()=>[...grid.selected],clearSelection:()=>grid.setSelection([]),focus:()=>grid.setFocus(grid.focused||grid.rowItem(grid.rowAt(grid.logicalTop())),true),refresh:()=>{grid.version++;grid.scheduleRender()},reload:()=>grid.setQuery(gridQueryStart(grid.acceptedQuery||grid.view.query)),destroy:()=>{grid.destroy();if(activeGrids.get(host)===api)activeGrids.delete(host);if(host.canopyx===api)delete host.canopyx}};for(const name of["getQuery","setQuery","sort","getPaging","goToPage","setPageLength","firstPage","previousPage","nextPage","setColumnWidth","autoFitColumn","autoFitColumns","autoSizeColumns","setColumnPriority","resetColumns","setColumnPinned","setColumnVisible","setColumnOrder","getState","setState","resetView","getSavedViews","saveView","applySavedView","removeSavedView","setTimeZone","setSelection","prepareExport","toCsv","copy","exportToHost","openDetails","scrollToRow","retry"])
892
- api[name]=grid[name].bind(grid);GRID_REPORTING.api?.(api,grid);Object.freeze(api);activeGrids.set(host,api);host.setAttribute("data-canopyx-mounted","true");host.canopyx=api;return api}
893
- function checkIntentPath(path){if(!Array.isArray(path)||!path.length)throw new Error("CanopyX: checking needs a complete node-ID path.");const seen=new Set();return path.map(id=>{requireIdentifier(id,"checking path ID");if(seen.has(id))throw new Error("CanopyX: a checking path cannot repeat an ID.");seen.add(id);return id})}
990
+ if(button.parentNode!==slot.cell)slot.cell.appendChild(button);button.dataset.cxRecord=row.id;button.setAttribute("aria-label",this.labels.gridDetails);setText(button,"+"+count);slot.hiddenFields=button};LINK_IMPL.DataGrid=DataGrid;LINK_IMPL.gridQueryStart=gridQueryStart;linkMethods(DataGrid);linkGridPart(DataGrid);function createDataGrid(host,options){if(!host||!options||!options.view)throw new Error("CanopyX: createDataGrid needs a host element and a grid document.");const existing=activeGrids.get(host);if(existing)return existing;const grid=new DataGrid(host,options);const api={element:host,view:grid.view,ready:startPlugins(grid,()=>api),getTimeZone:()=>grid.local?"local":"utc",getSelection:()=>[...grid.selected],clearSelection:()=>grid.setSelection([]),focus:()=>grid.setFocus(grid.focused||grid.rowItem(grid.rowAt(grid.logicalTop())),true),refresh:()=>{grid.version++;grid.scheduleRender()},reload:()=>grid.setQuery(gridQueryStart(grid.queryForChange(true)),grid.pendingPaging&&
991
+ {pageLength:grid.pendingPaging.pageLength,allRows:grid.pendingPaging.allRows}),destroy:()=>{grid.destroy();if(activeGrids.get(host)===api)activeGrids.delete(host);if(host.canopyx===api)delete host.canopyx}};for(const name of["getQuery","setQuery","sort","getPaging","goToPage","setPageLength","firstPage","previousPage","nextPage","setColumnWidth","autoFitColumn","autoFitColumns","autoSizeColumns","setColumnPriority","resetColumns","setColumnPinned","setColumnVisible","setColumnOrder","getState","setState","resetView","getSavedViews","saveView","applySavedView","removeSavedView","setTimeZone","setSelection","prepareExport","toCsv","copy","exportToHost","exportAs","openDetails","openRecord","scrollToRow","retry","closeMenus","getFormatRules","setFormatRules","openFormatRules","getLayout","setLayout","getMatches","setSearchHighlight","nextMatch","previousMatch",...GRID_EDIT_API,...GRID_LIVE_API,...GRID_CALENDAR_API,"getLiveState","getFacetPanel","setFacetPanelOpen"])
992
+ api[name]=(...args)=>grid[name](...args);for(const[,read,...names]of GRID_PART_API){api[read]=(...args)=>grid[read](...args);for(const name of names)api[name]=(...args)=>{try{return Promise.resolve(grid[name](...args))}catch(error){return Promise.reject(error)}}}
993
+ GRID_REPORTING.api?.(api,grid);Object.assign(api,linkApi(grid));api.ready=linkReady(grid,api.ready);Object.freeze(api);activeGrids.set(host,api);host.setAttribute("data-canopyx-mounted","true");host.canopyx=api;recordStart(grid);return api}
994
+ const GRID_ENHANCE_IMPL={},GRID_ENHANCE=definePart(DataGrid,"enhance","table enhancement",()=>({DataGrid,GRID_ENHANCE_IMPL,PROTOCOL,createDataGrid,h,setText,gridQueryRows,gridCompare,gridResultShape,isIdentifier,GRID_REPORTING,loadPart,GRID_PAGER}));const gridEnhancer=table=>(target,options)=>loadPart(GRID_ENHANCE).then(()=>GRID_ENHANCE_IMPL.create(target,options,table));const GRID_API={createDataGrid,createCalendar,enhance:gridEnhancer(true),grid:gridEnhancer(false)};Object.assign(VIEW_MODES,{DataGrid,gridMatches,gridResultShape});Object.assign(DataGrid.prototype,VIEW_MODE_METHODS);definePart(DataGrid,"highlight","search highlighting",viewModeContext);definePart(DataGrid,"layouts","layout modes",viewModeContext);const MODES_BASE={};for(const name of["prepareState","build","getState","readGridState","applyStatePresentation","initializeState"])MODES_BASE[name]=DataGrid.prototype[name];Object.assign(DataGrid.prototype,{prepareState(){this.prepareViewModes();const result=MODES_BASE.prepareState.call(this);this.modeState(this.defaultState);if(this.initialState)this.adoptModes(this.initialState);return result},build(){MODES_BASE.build.call(this);this.host.setAttribute("data-cx-density",this.density);this.lifetime.signal.addEventListener("abort",()=>this.host.removeAttribute("data-cx-layout"),{once:true});this.modesStarted=this.highlightOptions||this.layoutOptions?this.startViewModes():null},getState(){return this.modeState(MODES_BASE.getState.call(this))},readGridState(state){return Object.assign(MODES_BASE.readGridState.call(this,state),readModeState(this,state))},applyStatePresentation(state){const applied=MODES_BASE.applyStatePresentation.call(this,state);this.applyModeState(state);return applied},initializeState(){const ready=MODES_BASE.initializeState.call(this);return this.modesStarted?Promise.all([ready,this.modesStarted]).then(([result])=>result):ready}});function checkIntentPath(path){if(!Array.isArray(path)||!path.length)throw new Error("CanopyX: checking needs a complete node-ID path.");const seen=new Set();return path.map(id=>{requireIdentifier(id,"checking path ID");if(seen.has(id))throw new Error("CanopyX: a checking path cannot repeat an ID.");seen.add(id);return id})}
894
995
  function checkBranch(){return{value:null,mask:0,children:new Map()}}
895
996
  class CheckIntents{constructor(items,mode){if(!Array.isArray(items))throw new Error("CanopyX: checkIntents must be an array.");this.mode=mode;this.root=checkBranch();for(const item of items){if(!item||typeof item.checked!=="boolean")throw new Error("CanopyX: a checking intent needs a boolean checked value.");const path=checkIntentPath(item.path);let branch=this.root;for(const id of path){if(!branch.children.has(id))branch.children.set(id,checkBranch());branch=branch.children.get(id)}
896
997
  if(branch.value!==null)throw new Error("CanopyX: a checking path cannot have duplicate intents.");branch.value=item.checked}
@@ -933,8 +1034,8 @@ w&&!w.status&&!w.h.detail&&
933
1034
  super.onKey(event)}
934
1035
  getState(){const state=super.getState();if(this.checkMode){state.checkboxes=this.checkMode;state.checkIntents=this.checks.snapshot()}
935
1036
  return state}
936
- toOutline(){const model=this.checks,items=this.rows.filter(w=>!w.status).map(w=>
937
- ({path:this.pathOf(w),label:w.h.label,detail:!!w.h.detail}));const markup=super.toOutline();if(!this.checkMode)return markup;const holder=h("div");holder.innerHTML=markup;const rows=holder.querySelector("table").tBodies[0].rows;items.forEach((item,index)=>{if(item.detail)return;const value=model.state(item.path),marker=h("span");marker.setAttribute("role","checkbox");marker.setAttribute("aria-readonly","true");marker.setAttribute("aria-label",item.label);marker.setAttribute("aria-checked",value==="mixed"?"mixed":String(value));marker.style.marginRight="6px";marker.textContent=value==="mixed"?"▣ ":value?"☑ ":"☐ ";rows[index].cells[0].prepend(marker)});return holder.innerHTML}
1037
+ outlineElement(){const model=this.checks,items=this.rows.filter(w=>!w.status).map(w=>
1038
+ ({path:this.pathOf(w),label:w.h.label,detail:!!w.h.detail}));const table=super.outlineElement();if(!this.checkMode||!table)return table;const rows=table.tBodies[0].rows;items.forEach((item,index)=>{if(item.detail)return;const value=model.state(item.path),marker=h("span");marker.setAttribute("role","checkbox");marker.setAttribute("aria-readonly","true");marker.setAttribute("aria-label",item.label);marker.setAttribute("aria-checked",value==="mixed"?"mixed":String(value));marker.style.marginRight="6px";marker.textContent=value==="mixed"?"▣ ":value?"☑ ":"☐ ";rows[index].cells[0].prepend(marker)});return table}
938
1039
  readState(state){const result=super.readState(state);if(!result||!result.version)return result;if(state.checkboxes!==undefined&&state.checkboxes!==this.checkMode)return null;try{if(this.checkMode){result.checkboxes=this.checkMode;result.checkIntents=new CheckIntents(state.checkIntents===undefined?[]:state.checkIntents,this.checkMode).snapshot()}else if(state.checkIntents!==undefined&&(!Array.isArray(state.checkIntents)||state.checkIntents.length))return null;return result}catch(error){return null}}
939
1040
  async setState(state){const parsed=this.readState(state);if(!parsed||this.destroyed)return false;const revision=++this.checkRevision;if(parsed.checkIntents&&!this.voteCheck(new CheckIntents(parsed.checkIntents,this.checkMode),null,"restore",revision))return false;const operation=super.setState(parsed);this.paintCheckedRows();try{return await operation}
940
1041
  finally{this.paintCheckedRows()}}
@@ -970,10 +1071,10 @@ cancelInteraction(){const operation=this.pendingInteraction;if(!operation)return
970
1071
  abortInteractionController(controller){if(!controller||controller.signal.aborted)return;if(this.interactionReplacementDepth){if(!this.interactionAbortQueue)this.interactionAbortQueue=new Set();this.interactionAbortQueue.add(controller)}else controller.abort()}
971
1072
  withInteractionReplacement(work){this.interactionReplacementDepth=(this.interactionReplacementDepth||0)+ 1;try{return work()}
972
1073
  finally{this.interactionReplacementDepth--;if(!this.interactionReplacementDepth){const controllers=this.interactionAbortQueue;this.interactionAbortQueue=null;if(controllers)for(const controller of controllers)controller.abort()}}}
973
- destroy(){this.interactionClosing=true;this.cancelInteraction();super.destroy();this.interactions=null;if(this.interactionPins)this.interactionPins.clear()}};DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{actions:"Actions",editValue:"Edit {column}",saveEdit:"Save",cancelEdit:"Cancel",invalidEdit:"Enter a valid {column} value.",interactionPending:"Waiting for the host…",interactionRejected:"The host declined the operation.",interactionFailed:"Operation failed: {error}",markMove:"Move this node",moveHere:"Move here",moveBefore:"Move before",moveAfter:"Move after",cancelMove:"Cancel move"}));TreeGrid=class extends TreeGrid{build(){super.build();this.interactionUiLifetime=new AbortController();const signal=this.interactionUiLifetime.signal;this.grid.addEventListener("scroll",()=>this.closeInteractionPanel(false),{signal,passive:true});global.addEventListener("resize",()=>this.closeInteractionPanel(),{signal});document.addEventListener("pointerdown",event=>{if(this.interactionPanel&&!this.interactionPanel.contains(event.target))this.closeInteractionPanel(false)},{signal});this.grid.addEventListener("contextmenu",event=>{const row=event.target.closest(".cx-row");if(row&&row._w&&!hostControl(event.target)&&this.openActionMenu(row._w,row))event.preventDefault()},{signal})}
1074
+ destroy(){this.interactionClosing=true;this.cancelInteraction();super.destroy();this.interactions=null;if(this.interactionPins)this.interactionPins.clear()}};DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{actions:"Actions",editValue:"Edit {column}",saveEdit:"Save",cancelEdit:"Cancel",invalidEdit:"Enter a valid {column} value.",interactionPending:"Waiting for the host…",interactionRejected:"The host declined the operation.",interactionFailed:"Operation failed: {error}",markMove:"Move this node",moveHere:"Move here",moveBefore:"Move before",moveAfter:"Move after",cancelMove:"Cancel move"}));TreeGrid=class extends TreeGrid{build(){super.build();this.interactionUiLifetime=new AbortController();const signal=this.interactionUiLifetime.signal;this.grid.addEventListener("scroll",()=>{const panel=this.interactionPanel;if(panel&&(panel._cxScrollTop!==this.grid.scrollTop||panel._cxScrollLeft!==this.grid.scrollLeft))this.closeInteractionPanel(false)},{signal,passive:true});global.addEventListener("resize",()=>this.closeInteractionPanel(),{signal});document.addEventListener("pointerdown",event=>{if(this.interactionPanel&&!this.interactionPanel.contains(event.target))this.closeInteractionPanel(false)},{signal});this.grid.addEventListener("contextmenu",event=>{const row=event.target.closest(".cx-row");if(row&&row._w&&!hostControl(event.target)&&this.openActionMenu(row._w,row))event.preventDefault()},{signal})}
974
1075
  interactionApi(){return Object.assign(super.interactionApi(),{beginEdit:(id,column)=>this.beginEdit(id,column),openActions:id=>{const w=this.interactionNode(id);return!!w&&this.openActionMenu(w,this.rendered.get(w))}})}
975
1076
  openInteractionPanel(w,anchor,role,label){const revision=(this.uiWorkRevision||0)+(this.interactionPanel?1:0);this.closeInteractionPanel(false);if(this.destroyed||this.interactionClosing||this.interactionPanel||(this.uiWorkRevision||0)!==revision)return null;this.render();anchor=this.rendered.get(w);if(this.destroyed||this.interactionClosing||this.interactionPanel||(this.uiWorkRevision||0)!==revision||!anchor||!anchor.isConnected)return null;const panel=this.interactionPanel=h("div","cx-interaction-panel");panel.setAttribute("role",role);panel.setAttribute("aria-label",label);this.panelNode=w;this.host.appendChild(panel);panel.addEventListener("pointerdown",()=>{panel._cxPointerFocus=panel.contains(document.activeElement)});return panel}
976
- placeInteractionPanel(anchor){const panel=this.interactionPanel;if(!panel)return;const rect=anchor.getBoundingClientRect(),bounds=panel.getBoundingClientRect();panel.style.left=Math.max(8,Math.min(rect.left,global.innerWidth-bounds.width-8))+ "px";const below=global.innerHeight-rect.bottom-8,above=rect.top-8;const top=below>=Math.min(bounds.height,120)||below>=above?rect.bottom:rect.top-bounds.height;panel.style.top=Math.max(8,Math.min(top,global.innerHeight-bounds.height-8))+ "px"}
1077
+ placeInteractionPanel(anchor){const panel=this.interactionPanel;if(!panel)return;const rect=anchor.getBoundingClientRect(),bounds=panel.getBoundingClientRect();panel.style.left=Math.max(8,Math.min(rect.left,global.innerWidth-bounds.width-8))+ "px";const below=global.innerHeight-rect.bottom-8,above=rect.top-8;const top=below>=Math.min(bounds.height,120)||below>=above?rect.bottom:rect.top-bounds.height;panel.style.top=Math.max(8,Math.min(top,global.innerHeight-bounds.height-8))+ "px";panel._cxScrollTop=this.grid.scrollTop;panel._cxScrollLeft=this.grid.scrollLeft}
977
1078
  closeInteractionPanel(restoreFocus=true){const panel=this.interactionPanel,controller=this.editorController,w=this.panelNode;if(!panel)return null;const active=document.activeElement;const owned=panel.contains(active)||panel._cxPointerFocus&&(active===document.body||active===document.documentElement);if(controller&&this.uiPendingOperation&&this.uiPendingOperation.controller===controller){this.uiPendingOperation=null;setText(this.live,"")}
978
1079
  this.uiWorkRevision=(this.uiWorkRevision||0)+ 1;const focus=owned?{node:w,revision:this.uiWorkRevision}:null;this.interactionPanel=null;this.panelNode=null;this.editorController=null;panel.remove();this.abortInteractionController(controller);if(restoreFocus)this.restoreInteractionFocus(focus);return focus}
979
1080
  restoreInteractionFocus(focus){if(!focus||this.destroyed||this.interactionClosing||this.interactionPanel||focus.revision!==this.uiWorkRevision)return;const active=document.activeElement;if(active&&active!==document.body&&active!==document.documentElement&&active.isConnected)return;const visible=w=>w&&!w._invalidated&&this.keyMap.get(w.key)===w&&this.indexOf(w)>=0;const target=visible(focus.node)?focus.node:visible(this.focused)?this.focused:this.rows.find(visible);if(target)this.setFocus(target,true);else{this.grid.tabIndex=0;this.grid.focus({preventScroll:true})}}
@@ -1040,29 +1141,15 @@ throw new Error("CanopyX: facets need unique declared column identities.");if(op
1040
1141
  throw new Error("CanopyX: reporting ordering is keyed by column identity.");for(const[id,policy]of Object.entries(options.ordering)){const column=view.columns.find(item=>item&&item.id===id);if(!column||column.kind!=="text"||!column.sortable)throw new Error("CanopyX: ordering needs a sortable text column.");reportingTextOrder(policy)}};GRID_REPORTING.compare=view=>{const policies=new Map(Object.entries(view.reporting&&view.reporting.ordering||{}).map(([id,policy])=>[id,reportingTextOrder(policy)]));if(!policies.size)return gridCompare;return(left,right,column,direction)=>{const compare=policies.get(column.id);if(!compare)return gridCompare(left,right,column,direction);const a=gridScalar(left.cells[column.id],column),b=gridScalar(right.cells[column.id],column);if(a===null||b===null)return a===b?0:a===null?1:-1;const result=compare(String(a),String(b));return direction==="desc"?-result:result}};DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridPinColumn:"{column}: pinned region",gridPinNone:"Scroll",gridFilterGroupTitle:"Additional grouped conditions",gridAddFilterGroup:"Add condition group",gridFacetValues:"Values and counts from applied filters",gridFacetIncludeCurrent:"Include this column's applied filters",gridFacetLoad:"Load values",gridFacetReload:"Values changed or the request was cancelled. Load values again.",gridBetween:"between (inclusive)",gridRangeEnd:"Upper value (UTC for dates)",gridInvalidRange:"Enter an upper value greater than or equal to the lower value.",gridSuggestedCategory:"Suggested category",gridChooseCategory:"Choose a category",gridBooleanValue:"Boolean value",gridHealthy:"Healthy",gridValue:"Value (UTC for dates)",gridAddFilter:"Add filter",gridMoveEarlier:"Move {column} earlier",gridMoveLater:"Move {column} later",gridFitColumn:"Fit {column}"}));function reportingRangeRows(filters){const rows=[];for(let index=0;index<filters.length;index++){const lower=filters[index],upper=filters[index+1];if(lower.operator==="ge"&&upper&&upper.operator==="le"&&lower.column===upper.column){rows.push({column:lower.column,operator:"between",values:[lower.values[0],upper.values[0]]});index++}else rows.push(lower)}
1041
1142
  return rows}
1042
1143
  function reportingRangeValue(raw,column,labels,local){if(column.kind==="number"){if(!raw.trim())throw new Error(labels.gridInvalidFilter);const value=Number(raw);gridScalar(value,column);return value}
1043
- const value=/(?:Z|[+-]\d\d:\d\d)$/.test(raw) ? raw : local ? new Date(raw).toISOString() : raw + "Z";
1144
+ let value=raw;if(!/(?:Z|[+-]\d\d:\d\d)$/.test(raw)) {
1145
+ const moment=gridDateInputMoment(raw,local);if(!moment)throw new Error(labels.gridInvalidFilter);value=local?moment.toISOString():raw+"Z"}
1044
1146
  gridScalar(value,column);return value}
1045
1147
  GRID_REPORTING.installFilters=({gridDateInput})=>{const REPORT_FILTER_BASE={addGridFilter:DataGrid.prototype.addGridFilter,readGridFilter:DataGrid.prototype.readGridFilter};Object.assign(DataGrid.prototype,{addGridFilter(parent,predicate){if(!this.view.reporting)return REPORT_FILTER_BASE.addGridFilter.call(this,parent,predicate);const count=parent.children.length,range=predicate&&predicate.operator==="between";REPORT_FILTER_BASE.addGridFilter.call(this,parent,range?{column:predicate.column,operator:"ge",values:[predicate.values[0]]}:predicate);if(parent.children.length===count)return;const row=parent.lastElementChild,fields=row._filter,upper=h("input","cx-grid-filter-value"),choices=h("select"),boolean=h("select");const upperField=gridInputLabel(this.labels.gridConditionUpperValue,upper),categoryField=gridInputLabel(this.labels.gridSuggestedCategory,choices),booleanField=gridInputLabel(this.labels.gridBooleanValue,boolean);boolean.append(gridOption("true",this.labels.gridTrue),gridOption("false",this.labels.gridFalse));row.insertBefore(upperField,row.lastElementChild);row.insertBefore(categoryField,row.lastElementChild);row.insertBefore(booleanField,row.lastElementChild);fields.upper=upper;fields.boolean=boolean;const refresh=()=>{const column=this.columns.find(item=>item.id===fields.column.value),rangeKind=column.kind==="number"||column.kind==="datetime";if(rangeKind&&![...fields.operator.options].some(option=>option.value==="between"))
1046
1148
  fields.operator.appendChild(gridOption("between",this.labels.gridBetween));upper.type=column.kind==="datetime"?"datetime-local":"number";upper.step="any";upperField.hidden=fields.operator.value!=="between";upper.disabled=upperField.hidden;const categories=this.view.reporting.ordering&&this.view.reporting.ordering[column.id];categoryField.hidden=column.kind!=="text"||!categories||!categories.categories||!categories.categories.length||
1047
1149
  fields.type.value!=="string"||!["eq","ne"].includes(fields.operator.value);choices.textContent="";choices.appendChild(gridOption("",this.labels.gridChooseCategory));for(const value of categories&&categories.categories||[])choices.appendChild(gridOption(value,value));booleanField.hidden=column.kind!=="text"||fields.type.value!=="boolean"||!["eq","ne"].includes(fields.operator.value);if(!booleanField.hidden){boolean.value=["true","false"].includes(fields.input.value)?fields.input.value:"true";fields.input.value=boolean.value}
1048
1150
  fields.input.closest("label").hidden=!booleanField.hidden||["isNull","isNotNull","in"].includes(fields.operator.value)};fields.column.addEventListener("change",refresh);fields.operator.addEventListener("change",refresh);fields.type.addEventListener("change",refresh);upper.addEventListener("input",()=>{fields.edited=true});choices.addEventListener("change",()=>{fields.input.value=choices.value;fields.edited=true});boolean.addEventListener("change",()=>{fields.input.value=boolean.value;fields.edited=true});refresh();if(range){fields.operator.value="between";upper.value=upper.type==="datetime-local"?gridDateInput(predicate.values[1],this.local):String(predicate.values[1]);refresh();fields.rangeOriginal={column:predicate.column,values:predicate.values.slice(),inputs:[fields.input.value,upper.value]}}},readGridFilter(row){const fields=row._filter;if(!this.view.reporting||fields.operator.value!=="between")return REPORT_FILTER_BASE.readGridFilter.call(this,row);const column=this.columns.find(item=>item.id===fields.column.value);const original=fields.rangeOriginal,inputs=[fields.input.value,fields.upper.value];const values=inputs.map((raw,index)=>original&&original.column===column.id&&raw===original.inputs[index]?
1049
1151
  original.values[index]:reportingRangeValue(raw,column,this.labels,this.local));if(gridScalar(values[0],column)>gridScalar(values[1],column))throw new Error(this.labels.gridInvalidRange);return[{column:column.id,operator:"ge",values:[values[0]]},{column:column.id,operator:"le",values:[values[1]]}]}})};function reportingColumnFiltered(query,id){return gridColumnFiltered(query,id)}
1050
- DataGrid.prototype.syncReportingFilterHeaders=function(){};DataGrid.prototype.openReportingColumnFilter=function(id){return this.openColumnFilter(id,this.headRow.querySelector('[data-cx-filter-column="'+CSS.escape(id)+'"]:not([hidden]),[data-cx-column-menu="'+CSS.escape(id)+'"]'))};DataGrid.prototype.focusReportingFilterHeader=function(){this.headRow.querySelector('[data-cx-filter-column="'+CSS.escape(this.reportingFilterReturn||"")+'"]:not([hidden]),[data-cx-column-menu="'+CSS.escape(this.reportingFilterReturn||"")+'"]')?.focus({preventScroll:true})};function reportingFacetKey(value,column){if(value!==null&&typeof value==="object"||value===undefined)throw new Error("CanopyX: facet values are raw scalars.");const scalar=gridScalar(value,column);if(scalar===null)return "null";return column.kind==="datetime"?"d:"+scalar:typeof scalar==="string"?"s:"+foldText(scalar):
1051
- typeof scalar==="boolean"?"b:"+scalar:"n:"+scalar}
1052
- function reportingFacetProjection(query,column){return gridColumnProjection(query,column)}
1053
- function reportingFacetPage(input,grid,request){checkDocument(input,"gridFacetPage");const column=grid.view.columns.find(item=>item.id===request.column);if(input.view!==grid.view.id||input.revision!==request.query.revision||input.column!==request.column||
1054
- input.search!==request.search||input.includeCurrentFilter!==request.includeCurrentFilter||
1055
- !Number.isInteger(input.matched)||input.matched<0||input.matched>MAX_PROTOCOL_INTEGER||
1056
- !Number.isInteger(input.totalValues)||input.totalValues<0||input.totalValues>input.matched||
1057
- !Array.isArray(input.items)||input.items.length>request.limit||input.items.length>input.totalValues||
1058
- !input.items.length&&input.totalValues>0||input.truncated!==(input.items.length<input.totalValues))
1059
- throw new Error("CanopyX: facet response must match its accepted revision, projection and bounded counts.");const widened=!request.includeCurrentFilter&&
1060
- gridResultShape(reportingFacetProjection(request.query,request.column))!==gridResultShape(request.query);if(Number.isInteger(grid.snapshot.total)&&input.matched>grid.snapshot.total||Number.isInteger(grid.snapshot.matched)&&
1061
- (widened?input.matched<grid.snapshot.matched:input.matched!==grid.snapshot.matched))
1062
- throw new Error("CanopyX: facet counts contradict the known accepted dataset counts.");const keys=new Set();let occurrences=0;const items=input.items.map(item=>{if(!item||!Object.hasOwn(item,"value")||!Number.isInteger(item.count)||item.count<1||item.count>input.matched)
1063
- throw new Error("CanopyX: invalid facet value/count bucket.");const key=reportingFacetKey(item.value,column);if(keys.has(key))throw new Error("CanopyX: duplicate typed facet bucket.");keys.add(key);occurrences +=item.count;return{value:item.value,count:item.count}});if(occurrences>input.matched||!request.search&&!input.truncated&&occurrences!==input.matched)
1064
- throw new Error("CanopyX: facet buckets contradict the matching row count.");return gridFreeze({protocol:PROTOCOL,type:"gridFacetPage",view:input.view,revision:input.revision,column:input.column,search:input.search,includeCurrentFilter:input.includeCurrentFilter,matched:input.matched,totalValues:input.totalValues,truncated:input.truncated,items})}
1065
- const FACET_INLINE_SOURCE=inlineGridSource;inlineGridSource=view=>{const source=FACET_INLINE_SOURCE(view);if(!view.reporting?.facetColumns?.length)return source;const columns=new Map(view.columns.map(column=>[column.id,column]));source.facet=(request,options)=>{if(options.signal.aborted)throw new DOMException("Facet request cancelled.","AbortError");const column=view.columns.find(item=>item.id===request.column),query=request.includeCurrentFilter?request.query:
1152
+ DataGrid.prototype.syncReportingFilterHeaders=function(){};DataGrid.prototype.openReportingColumnFilter=function(id){return this.openColumnFilter(id,this.headRow.querySelector('[data-cx-filter-column="'+CSS.escape(id)+'"]:not([hidden]),[data-cx-column-menu="'+CSS.escape(id)+'"]'))};DataGrid.prototype.focusReportingFilterHeader=function(){this.headRow.querySelector('[data-cx-filter-column="'+CSS.escape(this.reportingFilterReturn||"")+'"]:not([hidden]),[data-cx-column-menu="'+CSS.escape(this.reportingFilterReturn||"")+'"]')?.focus({preventScroll:true})};const reportingFacetKey=gridFacetKey;const reportingFacetPage=(input,grid,request)=>validateGridFacetPage(input,grid.view,request,grid.snapshot);const reportingFacetProjection=(query,column)=>gridColumnProjection(query,column);const FACET_INLINE_SOURCE=inlineGridSource;inlineGridSource=view=>{const source=FACET_INLINE_SOURCE(view);if(!view.reporting?.facetColumns?.length)return source;const columns=new Map(view.columns.map(column=>[column.id,column]));source.facet=(request,options)=>{if(options.signal.aborted)throw new DOMException("Facet request cancelled.","AbortError");const column=view.columns.find(item=>item.id===request.column),query=request.includeCurrentFilter?request.query:
1066
1153
  reportingFacetProjection(request.query,column.id);const rows=gridQueryRows(view,query,columns,false),buckets=new Map(),needle=foldText(request.search);for(const row of rows){const scalar=gridScalar(row.cells[column.id],column),value=column.kind==="datetime"&&scalar!==null?new Date(scalar).toISOString():scalar;const key=reportingFacetKey(value,column),existing=buckets.get(key);if(existing){existing.count++;if(typeof value==="string"&&value<existing.value)existing.value=value}else buckets.set(key,{value,count:1})}
1067
1154
  const items=[...buckets.values()].filter(item=>foldText(item.value).includes(needle));items.sort((a,b)=>{if(a.value===null||b.value===null)return a.value===b.value?0:a.value===null?1:-1;if(column.kind!=="text")return gridOrder(gridScalar(a.value,column),gridScalar(b.value,column));if(typeof a.value!==typeof b.value)return typeof a.value==="boolean"?-1:1;return gridOrder(typeof a.value==="string"?foldText(a.value):a.value,typeof b.value==="string"?foldText(b.value):b.value)});return{protocol:PROTOCOL,type:"gridFacetPage",view:view.id,revision:"inline",column:column.id,search:request.search,includeCurrentFilter:request.includeCurrentFilter,matched:rows.length,totalValues:items.length,truncated:items.length>request.limit,items:items.slice(0,request.limit)}};return source};const LAYOUT_VALIDATE=GRID_REPORTING.validate,LAYOUT_PREPARE=DataGrid.prototype.prepareState;GRID_REPORTING.api=(api,grid)=>{api.autoFitColumn=id=>GRID_REPORTING.fitColumn(grid,id)};function reportingLayout(grid,column){const declared=column.kind?column:grid.columns.find(item=>item.id===column.id)||column,policy=grid.columnLayouts.get(column.id)||{};return{minimum:declared.minWidth??policy.minimum??40,maximum:declared.maxWidth??policy.maximum??MAX_COLUMN_WIDTH,flex:declared.flexWeight??(typeof declared.flex==="number"?declared.flex:policy.flex||0),priority:declared.priority===undefined?policy.priority:gridRank(declared),alwaysVisible:declared.alwaysVisible??policy.alwaysVisible,pin:declared.pinned===undefined?policy.pin:declared.pinned||"none"}}
1068
1155
  function reportingWidth(grid,column,width){const policy=reportingLayout(grid,column);return Math.min(policy.maximum,Math.max(policy.minimum,width))}
@@ -1083,23 +1170,23 @@ else if(["ArrowLeft","ArrowRight","Home"].includes(event.key))reportingManualWid
1083
1170
  const index=this.headCells.indexOf(cell);if(index<0)return;const box=cell.getBoundingClientRect();target=this.cols[index];after=latest>box.left+box.width / 2;
1084
1171
  cell.classList.add(after?"cx-drop-after":"cx-drop-before")};const scroll=()=>{const box=this.grid.getBoundingClientRect(),delta=latest<box.left+36?-12:latest>box.right-36?12:0;if(delta){this.grid.scrollLeft +=delta;locate()}
1085
1172
  frame=requestAnimationFrame(scroll)};const move=ev=>{if(ev.pointerId!==pointer)return;latest=ev.clientX;if(!ghost&&Math.hypot(latest-start,ev.clientY-startY)<5)return;if(!ghost){ghost=h("div","cx-column-drag");ghost.setAttribute("aria-hidden","true");setText(ghost,column.title);const theme=getComputedStyle(this.host);for(const token of["surface","text","accent","line"])ghost.style.setProperty("--cx-"+token,theme.getPropertyValue("--cx-"+token));document.body.appendChild(ghost);frame=requestAnimationFrame(scroll)}
1086
- ghost.style.left=ev.clientX+12+"px";ghost.style.top=ev.clientY+12+"px";locate()};const stop=()=>{global.removeEventListener("pointermove",move);global.removeEventListener("pointerup",end);global.removeEventListener("pointercancel",cancel);global.removeEventListener("blur",stop);global.removeEventListener("keydown",key);this.lifetime.signal.removeEventListener("abort",stop);cancelAnimationFrame(frame);if(ghost)ghost.remove();clear();if(this.cancelColumnDrag===stop)this.cancelColumnDrag=null};const end=ev=>{if(ev.pointerId!==pointer)return;const next=ghost&&target,direction=after;stop();if(next&&!this.destroyed)this.moveReportingColumn(column,next,direction)};const cancel=ev=>{if(ev.pointerId===pointer)stop()};const key=ev=>{if(ev.key==="Escape"){ev.preventDefault();stop()}};this.cancelColumnDrag=stop;this.lifetime.signal.addEventListener("abort",stop,{once:true});global.addEventListener("pointermove",move);global.addEventListener("pointerup",end);global.addEventListener("pointercancel",cancel);global.addEventListener("blur",stop);global.addEventListener("keydown",key)};const COLUMN_PINS=["none","left","right"],PLACEMENT_VALIDATE=GRID_REPORTING.validate,PLACEMENT_PREPARE=DataGrid.prototype.prepareState,PLACEMENT_STATE_COLUMN=gridStateColumn,PLACEMENT_STATE_COLUMNS=gridStateColumns,PLACEMENT_API=GRID_REPORTING.api;GRID_REPORTING.validate=view=>{PLACEMENT_VALIDATE(view);const options=view.reporting;if(options.responsive!==undefined&&typeof options.responsive!=="boolean"||options.minimumCenterWidth!==undefined&&
1173
+ ghost.style.left=ev.clientX+12+"px";ghost.style.top=ev.clientY+12+"px";locate();this.groupBar?.classList.toggle("cx-group-drop",!!this.groupDrop&&this.groupBar.contains(document.elementFromPoint(ev.clientX,ev.clientY)))};const stop=()=>{global.removeEventListener("pointermove",move);global.removeEventListener("pointerup",end);global.removeEventListener("pointercancel",cancel);global.removeEventListener("blur",stop);global.removeEventListener("keydown",key);this.lifetime.signal.removeEventListener("abort",stop);cancelAnimationFrame(frame);if(ghost)ghost.remove();clear();this.groupBar?.classList.remove("cx-group-drop");if(this.cancelColumnDrag===stop)this.cancelColumnDrag=null};const end=ev=>{if(ev.pointerId!==pointer)return;const next=ghost&&target,direction=after,grouped=ghost&&this.groupBar?.contains(document.elementFromPoint(ev.clientX,ev.clientY));stop();if(grouped&&!this.destroyed&&this.groupDrop?.(column))return;if(next&&!this.destroyed)this.moveReportingColumn(column,next,direction)};const cancel=ev=>{if(ev.pointerId===pointer)stop()};const key=ev=>{if(ev.key==="Escape"){ev.preventDefault();stop()}};this.cancelColumnDrag=stop;this.lifetime.signal.addEventListener("abort",stop,{once:true});global.addEventListener("pointermove",move);global.addEventListener("pointerup",end);global.addEventListener("pointercancel",cancel);global.addEventListener("blur",stop);global.addEventListener("keydown",key)};const COLUMN_PINS=["none","left","right"],PLACEMENT_VALIDATE=GRID_REPORTING.validate,PLACEMENT_PREPARE=DataGrid.prototype.prepareState,PLACEMENT_STATE_COLUMN=gridStateColumn,PLACEMENT_STATE_COLUMNS=gridStateColumns,PLACEMENT_API=GRID_REPORTING.api;GRID_REPORTING.validate=view=>{PLACEMENT_VALIDATE(view);const options=view.reporting;if(options.responsive!==undefined&&typeof options.responsive!=="boolean"||options.minimumCenterWidth!==undefined&&
1087
1174
  (!Number.isInteger(options.minimumCenterWidth)||options.minimumCenterWidth<40||options.minimumCenterWidth>4096))
1088
1175
  throw new Error("CanopyX: invalid responsive flag or minimum center width.");for(const policy of Object.values(options.layout||{})){if(policy.pin!==undefined&&!COLUMN_PINS.includes(policy.pin)||policy.priority!==undefined&&
1089
1176
  (!Number.isInteger(policy.priority)||policy.priority<0||policy.priority>MAX_PROTOCOL_INTEGER)||
1090
1177
  policy.alwaysVisible!==undefined&&typeof policy.alwaysVisible!=="boolean")
1091
1178
  throw new Error("CanopyX: invalid reporting placement policy.")}};DataGrid.prototype.prepareState=function(){const policies=this.view.reporting&&this.view.reporting.layout||{};for(const column of this.columns)column.pin=policies[column.id]?.pin??column.pin;return PLACEMENT_PREPARE.call(this)};gridStateColumn=column=>Object.assign(PLACEMENT_STATE_COLUMN(column),{pin:column.pin});gridStateColumns=(grid,input)=>{const result=PLACEMENT_STATE_COLUMNS(grid,input),sources=new Map(input.map(column=>[column.id,column]));for(const saved of result){const source=sources.get(saved.id)||saved;if(source.pin!==undefined&&!COLUMN_PINS.includes(source.pin))throw new Error("CanopyX: invalid saved column pin.");saved.pin=source.pin===undefined?reportingLayout(grid,saved).pin||"none":source.pin}
1092
1179
  return result};GRID_REPORTING.api=(api,grid)=>{PLACEMENT_API(api,grid);api.setColumnPinned=(id,pin)=>grid.setColumnPinned(id,pin);api.getColumnLayout=()=>grid.columns.map(column=>{const hiddenReason=column.hidden?"user":column.responsiveHidden?"priority":column.fitHidden?
1093
- grid.phone&&gridLevel(column)===1?"phone":"priority":null;return Object.freeze({id:column.id,requestedPin:column.pin,pin:column.effectivePin||"none",visible:!hiddenReason,hiddenReason,responsiveHidden:!!column.responsiveHidden,width:column.renderWidth||column.width})})};DataGrid.prototype.setColumnPinned=function(id,pin){const column=this.columns.find(item=>item.id===id);if(this.destroyed||!column||!COLUMN_PINS.includes(pin))throw new Error("CanopyX: pin needs a live grid, declared column and none, left or right.");if(this.cancelResize)this.cancelResize();const held=this.heldCell(),columnId=held&&this.cols[held.index]&&this.cols[held.index].id;column.pin=pin;this.buildHeader();this.syncColumnControls();this.render();this.restoreReportingFocus(held,columnId);this.stateChanged();this.emit("columnPin",{column:id,pin})};const PLACEMENT_WIDTHS=DataGrid.prototype.applyWidths,PLACEMENT_CELL=DataGrid.prototype.newCell;DataGrid.prototype.visibleColumns=function(){const requested=this.columns.filter(column=>!column.hidden),options=this.view.reporting||{};for(const column of this.columns)column.responsiveHidden=false;if(options.responsive&&this.grid&&this.grid.clientWidth){let total=requested.reduce((sum,column)=>sum+reportingWidth(this,column,column.width),0),remaining=requested.length;const candidates=requested.filter(column=>!reportingLayout(this,column).alwaysVisible).sort((a,b)=>
1180
+ grid.phone&&gridLevel(column)===1?"phone":"priority":null;return Object.freeze({id:column.id,requestedPin:column.pin,pin:column.effectivePin||"none",visible:!hiddenReason,hiddenReason,responsiveHidden:!!column.responsiveHidden,width:column.renderWidth||column.width})})};DataGrid.prototype.setColumnPinned=function(id,pin){const column=this.columns.find(item=>item.id===id);if(this.destroyed||!column||!COLUMN_PINS.includes(pin))throw new Error("CanopyX: pin needs a live grid, declared column and none, left or right.");if(this.cancelResize)this.cancelResize();const held=this.heldCell(),columnId=held&&this.cols[held.index]&&this.cols[held.index].id;column.pin=pin;this.buildHeader();this.syncColumnControls();this.render();this.restoreReportingFocus(held,columnId);this.stateChanged();this.emit("columnPin",{column:id,pin})};const PLACEMENT_WIDTHS=DataGrid.prototype.applyWidths,PLACEMENT_CELL=DataGrid.prototype.newCell;DataGrid.prototype.visibleColumns=function(){const requested=this.columns.filter(column=>!column.hidden),options=this.view.reporting||{};for(const column of this.columns)column.responsiveHidden=false;if(options.responsive&&this.grid&&this.grid.clientWidth&&(this.layoutMode||"fit")==="fit"){let total=requested.reduce((sum,column)=>sum+reportingWidth(this,column,column.width),0),remaining=requested.length;const candidates=requested.filter(column=>!reportingLayout(this,column).alwaysVisible).sort((a,b)=>
1094
1181
  (reportingLayout(this,b).priority??MAX_PROTOCOL_INTEGER)-(reportingLayout(this,a).priority??MAX_PROTOCOL_INTEGER)||
1095
1182
  requested.indexOf(b)- requested.indexOf(a));for(const column of candidates){if(total<=this.grid.clientWidth||remaining===1)break;column.responsiveHidden=true;total -=reportingWidth(this,column,column.width);remaining--}}
1096
1183
  const visible=requested.filter(column=>!column.responsiveHidden&&!column.fitHidden);return["left","none","right"].flatMap(pin=>visible.filter(column=>column.pin===pin))};DataGrid.prototype.applyWidths=function(){this.fitLayout();const visible=this.visibleColumns();if(this.fitBuilding&&this.cols&&(visible.length!==this.cols.length||visible.some((column,index)=>column!==this.cols[index])))
1097
1184
  queueMicrotask(()=>this.destroyed||this.applyWidths());else if(this.cols&&(visible.length!==this.cols.length||visible.some((column,index)=>column!==this.cols[index]))){const held=this.heldCell(),columnId=held&&this.cols[held.index]&&this.cols[held.index].id;const heldHeader=this.headRow.contains(document.activeElement);this.buildHeader();this.syncColumnControls();this.render();this.restoreReportingFocus(held,columnId);if(heldHeader&&document.activeElement===document.body){if(this.focused)this.setFocus(this.focused,true);else this.focusReportingHeader(this.headRow.querySelector("button,.cx-resize"))}
1098
1185
  return}
1099
1186
  PLACEMENT_WIDTHS.call(this);gridPinGeometry(this);const active=document.activeElement;if(active&&this.headRow.contains(active))this.focusReportingHeader(active);const cell=active&&active.closest(".cx-cell");if(cell&&this.canvas.contains(cell))this.revealInRow(cell,active,false)};DataGrid.prototype.newCell=function(index,width,kind){const cell=PLACEMENT_CELL.call(this,index,width,kind);if(kind&&this.cols[index])this.applyColumnPin(cell,this.cols[index]);return cell};function reportingColumnOrder(columns){return["left","none","right"].flatMap(pin=>columns.filter(column=>column.pin===pin))}
1100
- const PLACEMENT_HEADER=DataGrid.prototype.buildHeader,PLACEMENT_MOVE=DataGrid.prototype.moveReportingColumn;DataGrid.prototype.buildHeader=function(){this.columns=reportingColumnOrder(this.columns);return PLACEMENT_HEADER.call(this)};DataGrid.prototype.moveReportingColumn=function(column,target,after){column.pin=target.pin;return PLACEMENT_MOVE.call(this,column,target,after)};DataGrid.prototype.setColumnVisible=function(id,visible){const column=this.columns.find(item=>item.id===id);if(!column||typeof visible!=="boolean")throw new Error("CanopyX: visibility needs a declared column and boolean.");if(!visible&&!column.hidden&&this.columns.filter(item=>!item.hidden).length===1)throw new Error("CanopyX: keep at least one column visible.");const held=this.heldCell(),columnId=held&&this.cols[held.index]&&this.cols[held.index].id;column.hidden=!visible;this.buildHeader();this.syncColumnControls();this.render();this.restoreReportingFocus(held,columnId);this.stateChanged()};const PLACEMENT_REVEAL=DataGrid.prototype.revealInRow,PLACEMENT_FOCUS_HEADER=DataGrid.prototype.focusReportingHeader,PLACEMENT_EXPORT=DataGrid.prototype.prepareExport,PLACEMENT_ACCESS_HEADER=DataGrid.prototype.buildHeader;function revealReportingCenter(grid,target,pin){if(pin&&pin!=="none")return;const frame=grid.grid.getBoundingClientRect(),left=frame.left+grid.grid.clientLeft +(grid.pinnedLeftWidth||0),right=frame.left+grid.grid.clientLeft+grid.grid.clientWidth -(grid.pinnedRightWidth||0),box=target.getBoundingClientRect();if(box.left<left)grid.grid.scrollLeft -=left-box.left;else if(box.right>right)grid.grid.scrollLeft +=Math.min(box.right-right,Math.max(0,box.left-left))}
1187
+ const PLACEMENT_HEADER=DataGrid.prototype.buildHeader,PLACEMENT_MOVE=DataGrid.prototype.moveReportingColumn;DataGrid.prototype.buildHeader=function(){this.columns=reportingColumnOrder(this.columns);return PLACEMENT_HEADER.call(this)};DataGrid.prototype.moveReportingColumn=function(column,target,after){column.pin=target.pin;return PLACEMENT_MOVE.call(this,column,target,after)};DataGrid.prototype.setColumnVisible=function(id,visible){const column=this.columns.find(item=>item.id===id);if(!column||typeof visible!=="boolean")throw new Error("CanopyX: visibility needs a declared column and boolean.");if(!visible&&!column.hidden&&this.columns.filter(item=>!item.hidden).length===1)throw new Error("CanopyX: keep at least one column visible.");const held=this.heldCell(),columnId=held&&this.cols[held.index]&&this.cols[held.index].id;column.hidden=!visible;this.buildHeader();this.syncColumnControls();this.render();this.restoreReportingFocus(held,columnId);this.stateChanged()};const PLACEMENT_REVEAL=DataGrid.prototype.revealInRow,PLACEMENT_FOCUS_HEADER=DataGrid.prototype.focusReportingHeader,PLACEMENT_ACCESS_HEADER=DataGrid.prototype.buildHeader;function revealReportingCenter(grid,target,pin){if(pin&&pin!=="none")return;const frame=grid.grid.getBoundingClientRect(),left=frame.left+grid.grid.clientLeft +(grid.pinnedLeftWidth||0),right=frame.left+grid.grid.clientLeft+grid.grid.clientWidth -(grid.pinnedRightWidth||0),box=target.getBoundingClientRect();if(box.left<left)grid.grid.scrollLeft -=left-box.left;else if(box.right>right)grid.grid.scrollLeft +=Math.min(box.right-right,Math.max(0,box.left-left))}
1101
1188
  DataGrid.prototype.revealInRow=function(cell,target,treeCell){PLACEMENT_REVEAL.call(this,cell,target,treeCell);if(cell.parentNode&&cell.parentNode._kind==="node")revealReportingCenter(this,target,cell.getAttribute("data-cx-pin"))};DataGrid.prototype.focusReportingHeader=function(control){PLACEMENT_FOCUS_HEADER.call(this,control);if(control)revealReportingCenter(this,control,control.closest(".cx-hcell").getAttribute("data-cx-pin"))};DataGrid.prototype.buildHeader=function(){if(!this.pinnedHeaderFocus){this.pinnedHeaderFocus=true;this.headRow.addEventListener("focusin",event=>{const cell=event.target.closest(".cx-hcell");if(cell&&this.headRow.contains(cell))revealReportingCenter(this,event.target,cell.getAttribute("data-cx-pin"))},{signal:this.lifetime.signal})}
1102
- return PLACEMENT_ACCESS_HEADER.call(this)};DataGrid.prototype.prepareExport=function(options){const columns=options&&options.columns===undefined?reportingColumnOrder(this.columns.filter(column=>!column.hidden)).map(column=>column.id):undefined;return PLACEMENT_EXPORT.call(this,columns?Object.assign({},options,{columns}):options)};const PRESENTERS_VALIDATE=GRID_REPORTING.validate;GRID_REPORTING.validate=view=>{PRESENTERS_VALIDATE(view);const presenters=view.reporting.presenters;if(presenters===undefined)return;if(!presenters||typeof presenters!=="object"||Array.isArray(presenters))throw new Error("CanopyX: presenters are keyed by declared column ID.");for(const[id,options]of Object.entries(presenters)){const column=view.columns.find(item=>item&&item.id===id);if(!column)throw new Error("CanopyX: a presenter needs a declared column.");gridPresenter(options,column,view.columns)}};const FACET_API=GRID_REPORTING.api;GRID_REPORTING.api=(api,grid)=>{FACET_API?.(api,grid);api.getFacets=(column,options)=>grid.getFacets(column,options)};DataGrid.prototype.getFacets=async function(column,options={}){if(this.destroyed||this.pendingQuery||!this.acceptedQuery||!this.view.reporting?.facetColumns?.includes(column)||typeof this.source.facet!=="function")
1189
+ const result=PLACEMENT_ACCESS_HEADER.call(this);this.syncPrintAvailability?.();return result};DataGrid.prototype.defaultExportColumns=function(){return reportingColumnOrder(this.columns.filter(column=>!column.hidden&&column.exportable!==false))};const PRESENTERS_VALIDATE=GRID_REPORTING.validate;GRID_REPORTING.validate=view=>{PRESENTERS_VALIDATE(view);const presenters=view.reporting.presenters;if(presenters===undefined)return;if(!presenters||typeof presenters!=="object"||Array.isArray(presenters))throw new Error("CanopyX: presenters are keyed by declared column ID.");for(const[id,options]of Object.entries(presenters)){const column=view.columns.find(item=>item&&item.id===id);if(!column)throw new Error("CanopyX: a presenter needs a declared column.");gridPresenter(options,column,view.columns)}};const FACET_API=GRID_REPORTING.api;GRID_REPORTING.api=(api,grid)=>{FACET_API?.(api,grid);api.getFacets=(column,options)=>grid.getFacets(column,options)};DataGrid.prototype.getFacets=async function(column,options={}){if(this.destroyed||this.pendingQuery||!this.acceptedQuery||!this.view.reporting?.facetColumns?.includes(column)||typeof this.source.facet!=="function")
1103
1190
  throw new Error("CanopyX: facets need an accepted query, opted-in column and explicit source capability.");const search=options.search===undefined?"":options.search,limit=options.limit===undefined?100:options.limit,includeCurrentFilter=options.includeCurrentFilter===undefined?true:options.includeCurrentFilter;if(typeof search!=="string"||search.length>16384||!Number.isInteger(limit)||limit<1||limit>200||typeof includeCurrentFilter!=="boolean")
1104
1191
  throw new Error("CanopyX: invalid bounded facet request.");const controller=new AbortController(),dataSignal=this.dataLifetime.signal,epoch=this.queryEpoch;const abort=()=>controller.abort();dataSignal.addEventListener("abort",abort,{once:true});options.signal?.addEventListener("abort",abort,{once:true});if(options.signal?.aborted)abort();const request=gridFreeze({protocol:PROTOCOL,type:"gridFacetQuery",column,search,limit,includeCurrentFilter,query:this.acceptedQuery});try{const page=await this.runGridHook(signal=>this.source.facet(request,{signal}),controller.signal);gridOperationActive(this,controller.signal);if(epoch!==this.queryEpoch)throw new DOMException("Facet request was superseded.","AbortError");return reportingFacetPage(page,this,request)}finally{dataSignal.removeEventListener("abort",abort);options.signal?.removeEventListener("abort",abort);controller.abort()}};const VIEW_SETTINGS_LIMIT=1048576,VIEW_STATE_BASE={};for(const name of["prepareState","readStoredState","writeStoredState","saveView","removeSavedView","resetView"])VIEW_STATE_BASE[name]=DataGrid.prototype[name];function reportingStoredSettings(grid,input){const json=JSON.stringify(input);if(typeof json!=="string"||json.length>VIEW_SETTINGS_LIMIT)throw new Error("CanopyX: view preferences exceed the 1,048,576-character limit.");const value=JSON.parse(json);if(!value||Array.isArray(value)||Object.keys(value).some(key=>!["version","view","current","views","defaultView"].includes(key))||
1105
1192
  (value.version!==undefined||value.view!==undefined)&&(value.version!==1||value.view!==grid.view.id)||
@@ -1120,7 +1207,7 @@ catch(error){this.viewSettingsIntent=intent;throw error}},removeSavedView(name){
1120
1207
  return this.getQuery()},getShareState(){const state=this.getState();state.query=gridQuery(this.view,gridQueryStart({sort:state.query.sort,searchMode:state.query.searchMode}));state.selection=[];state.scrollTop=0;if(state.paging)state.paging.offset=0;return gridFreeze(state)}});const VIEW_STORE_INITIALIZE=DataGrid.prototype.initializeState,VIEW_STORE_QUERY=DataGrid.prototype.setQuery,VIEW_STORE_SEARCH=DataGrid.prototype.buildGridSearch,VIEW_STORE_STATE=DataGrid.prototype.setState,VIEW_STORE_PRESENTATION=DataGrid.prototype.applyStatePresentation,VIEW_STORE_API=GRID_REPORTING.api;GRID_REPORTING.api=(api,grid)=>{VIEW_STORE_API?.(api,grid);for(const name of["getViewSettings","getDefaultView","setDefaultView","renameSavedView","getShareState","flushViewSettings"])
1121
1208
  api[name]=grid[name].bind(grid)};GRID_REPORTING.search=(grid,update)=>{grid.scheduleGridSearch=delay=>{clearTimeout(grid.searchTimer);grid.searchTimer=setTimeout(update,delay)}};for(const name of["setColumnWidth","setColumnVisible","setColumnOrder","setColumnPinned","setTimeZone","setSelection"]){const original=DataGrid.prototype[name];DataGrid.prototype[name]=function(...args){const result=original.apply(this,args);this.noteViewPreferenceIntent();return result}}
1122
1209
  Object.assign(DataGrid.prototype,{noteViewPreferenceIntent(){this.viewPreferenceIntent++},buildGridSearch(){VIEW_STORE_SEARCH.call(this);for(const event of["input","compositionstart","compositionend"])
1123
- this.searchInput.addEventListener(event,()=>{this.noteViewPreferenceIntent();this.searchDeadline=performance.now()+ this.searchDelayMs},{signal:this.lifetime.signal})},setState(input){this.restorePreferenceIntent=this.viewPreferenceIntent;return VIEW_STORE_STATE.call(this,input)},applyStatePresentation(state){if(this.restorePreferenceIntent===this.viewPreferenceIntent)VIEW_STORE_PRESENTATION.call(this,state)},setQuery(input,paging){const pending=VIEW_STORE_QUERY.call(this,input,paging);this.viewQueryPromise=pending;return pending},async initializeState(){if(!this.opts.viewStore)return VIEW_STORE_INITIALIZE.call(this);this.loadingViewSettings=true;this.setViewPreferenceError(this.labels.loading);const epoch=this.queryEpoch,intent=this.viewSettingsIntent,preference=this.viewPreferenceIntent;try{const input=await this.runGridHook(signal=>this.opts.viewStore.load({key:this.storageKey,view:this.view.id,signal}));gridOperationActive(this);this.setViewPreferenceError("");if(input!==null&&input!==undefined&&intent===this.viewSettingsIntent){const settings=reportingStoredSettings(this,input);this.savedViews=new Map(settings.views.map(entry=>[entry.name,entry.state]));this.defaultSavedView=settings.defaultView;if(epoch===this.queryEpoch&&preference===this.viewPreferenceIntent)
1210
+ this.searchInput.addEventListener(event,()=>{this.noteViewPreferenceIntent();this.searchDeadline=performance.now()+ this.searchDelayMs},{signal:this.lifetime.signal})},setState(input){this.restorePreferenceIntent=this.viewPreferenceIntent;return VIEW_STORE_STATE.call(this,input)},applyStatePresentation(state){if(this.restorePreferenceIntent===this.viewPreferenceIntent)return VIEW_STORE_PRESENTATION.call(this,state)},setQuery(input,paging){const pending=VIEW_STORE_QUERY.call(this,input,paging);this.viewQueryPromise=pending;return pending},async initializeState(){if(!this.opts.viewStore)return VIEW_STORE_INITIALIZE.call(this);this.loadingViewSettings=true;this.setViewPreferenceError(this.labels.loading);const epoch=this.queryEpoch,intent=this.viewSettingsIntent,preference=this.viewPreferenceIntent;try{const input=await this.runGridHook(signal=>this.opts.viewStore.load({key:this.storageKey,view:this.view.id,signal}));gridOperationActive(this);this.setViewPreferenceError("");if(input!==null&&input!==undefined&&intent===this.viewSettingsIntent){const settings=reportingStoredSettings(this,input);this.savedViews=new Map(settings.views.map(entry=>[entry.name,entry.state]));this.defaultSavedView=settings.defaultView;if(epoch===this.queryEpoch&&preference===this.viewPreferenceIntent)
1124
1211
  this.initialState=this.defaultSavedView?this.savedViews.get(this.defaultSavedView):settings.current;this.syncSavedViews();this.setViewPreferenceError(this.viewPreferenceError||"")}}catch(error){if(this.destroyed)throw error;this.viewStoreFaulted=true;this.setViewPreferenceError(error.message)}finally{this.loadingViewSettings=false}
1125
1212
  let pending=this.viewQueryPromise;if(epoch===this.queryEpoch){const deadline=this.searchTimer==null?null:this.searchDeadline,text=this.searchInput?.value;pending=VIEW_STORE_INITIALIZE.call(this);if(deadline!==null){this.searchInput.value=text;this.scheduleGridSearch(Math.max(0,deadline-performance.now()))}}
1126
1213
  while(pending){try{await pending;if(pending===this.viewQueryPromise)return this.getQuery()}
@@ -1156,10 +1243,10 @@ if(this.selectionAnchor==null)this.selectionAnchor=start;return this.getSelectio
1156
1243
  const excluded=new Set(request.querySelection.excludedIds);const filtered={...request,scope:"filtered"};gridCopyExportPresentation(request,filtered);for await(const row of SELECTION_EXPORT_ROWS.call(this,filtered,signal))
1157
1244
  if(!excluded.delete(row.id))yield row;if(excluded.size)throw new Error("CanopyX: excluded records are absent from the accepted query revision.")};DataGrid.prototype.exportText=async function(exported,maxRows=50000,delimiter,signal){const selection=exported.request.querySelection;if(selection&&selection.matched!==null&&Number.isInteger(maxRows)&&maxRows>=1&&maxRows<=1000000&&
1158
1245
  selection.matched-selection.excludedIds.length>maxRows)
1159
- throw new Error("CanopyX: export exceeds the client row limit; use a host export hook.");return SELECTION_EXPORT_TEXT.call(this,exported,maxRows,delimiter,signal)};DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridSelectionQueryCount:"{count} matching records selected; {excluded} excluded",gridSelectRecord:"Select record {id}",gridSelection:"Selection",gridSelectPage:"Select page",gridSelectMatching:"Select all matching"}));const SELECTION_CONTROLS_BASE={};for(const name of["buildGridControls","fillRow","refreshRowState","onClick","onKey","setQuery"])
1160
- SELECTION_CONTROLS_BASE[name]=DataGrid.prototype[name];Object.assign(DataGrid.prototype,{buildGridControls(){SELECTION_CONTROLS_BASE.buildGridControls.call(this);if(!this.view.reporting?.selectionControls)return;const controls=h("div","cx-grid-selection-controls"),status=this.selectionStatus=h("span","cx-grid-selection-status");controls.setAttribute("role","group");controls.setAttribute("aria-label",this.labels.gridSelection);status.setAttribute("role","status");const page=this.multi&&this.paged?gridButton(this.labels.gridSelectPage,()=>this.runReportingSelection(()=>this.selectPage())):null;const all=this.multi?gridButton(this.labels.gridSelectMatching,()=>this.runReportingSelection(()=>this.selectAllMatching())):null;const clear=gridButton(this.labels.gridClearSelection,()=>this.setSelection([]));this.selectionButtons=[page,all,clear].filter(Boolean);controls.append(...this.selectionButtons,status);this.selectionControls=controls;this.syncSelectionControls()},syncSelectionControls(){if(!this.selectionStatus)return;for(const button of this.selectionButtons)button.disabled=!!this.selectionControlBusy||!this.acceptedQuery||!!this.pendingQuery;for(const[row,element]of this.rendered)if(!row.status){const check=element.querySelector(".cx-grid-row-select");if(check){check.checked=this.reportingRowSelected(row);check.disabled=!!this.pendingQuery}}
1246
+ throw new Error("CanopyX: export exceeds the client row limit; use a host export hook.");return SELECTION_EXPORT_TEXT.call(this,exported,maxRows,delimiter,signal)};DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridSelectionQueryCount:"{count} matching records selected; {excluded} excluded",gridSelectRecord:"Select record {id}",gridSelection:"Selection",gridSelectPage:"Select page",gridSelectMatching:"Select all matching"}));const SELECTION_CONTROLS_BASE={};for(const name of["buildGridControls","fillRow","refreshRowState","onClick","onKey","setQuery","closeMenusOutside"])
1247
+ SELECTION_CONTROLS_BASE[name]=DataGrid.prototype[name];Object.assign(DataGrid.prototype,{closeMenusOutside(node){SELECTION_CONTROLS_BASE.closeMenusOutside.call(this,node);const menu=this.headRow?.querySelector(".cx-header-selection");if(menu?.open&&!menu.contains(node))menu.open=false},buildGridControls(){SELECTION_CONTROLS_BASE.buildGridControls.call(this);if(!this.view.reporting?.selectionControls)return;const controls=h("div","cx-grid-selection-controls"),status=this.selectionStatus=h("span","cx-grid-selection-status");controls.setAttribute("role","group");controls.setAttribute("aria-label",this.labels.gridSelection);status.setAttribute("role","status");const page=this.multi&&this.paged?gridButton(this.labels.gridSelectPage,()=>this.runReportingSelection(()=>this.selectPage())):null;const all=this.multi?gridButton(this.labels.gridSelectMatching,()=>this.runReportingSelection(()=>this.selectAllMatching())):null;const clear=gridButton(this.labels.gridClearSelection,()=>this.setSelection([]));this.selectionButtons=[page,all,clear].filter(Boolean);controls.append(...this.selectionButtons,status);this.selectionControls=controls;this.syncSelectionControls()},syncSelectionControls(){if(!this.selectionStatus)return;for(const button of this.selectionButtons)button.disabled=!!this.selectionControlBusy||!this.acceptedQuery||!!this.pendingQuery;for(const[row,element]of this.rendered)if(!row.status){const check=element.querySelector(".cx-grid-row-select");if(check){check.checked=this.reportingRowSelected(row);check.disabled=!!this.pendingQuery}}
1161
1248
  const query=this.querySelection,format=numberFormat(this.lang);setText(this.selectionStatus,query?fill(query.matched===null?this.labels.gridSelectionQueryUnknown:this.labels.gridSelectionQueryCount,{count:query.matched===null?"":format.format(Math.max(0,query.matched-this.selectionExcluded.size)),excluded:format.format(this.selectionExcluded.size)}):
1162
- fill(this.labels.gridSelectionCount,{count:format.format(this.selected.size)}));const header=this.headRow?.querySelector(".cx-header-check"),count=query?query.matched===null?null:query.matched-this.selectionExcluded.size:this.selected.size;if(header){header.dataset.cxChecked=count===0?"false":count===this.snapshot?.matched?"true":"mixed";header.setAttribute("aria-label",this.labels.gridSelection+" · "+this.selectionStatus.textContent)}},buildHeaderSelection(){this.headerSelectionFlyout?.dispose();if(!this.selectionControls||!this.headCells[0])return;const menu=h("details","cx-columns cx-header-selection"),summary=h("summary"),panel=h("div","cx-columns-list cx-grid-panel");summary.setAttribute("aria-label",this.labels.gridSelection);summary.classList.add("cx-header-check");summary.setAttribute("aria-haspopup","menu");summary.appendChild(gridIcon("check"));panel.appendChild(this.selectionControls);menu.append(summary,panel);this.headCells[0].prepend(menu);const flyout=this.headerSelectionFlyout=watchFlyout(menu,panel,()=>menu.open);panel.popover="manual";menu.addEventListener("toggle",()=>{if(menu.open){if(panel.isConnected)panel.showPopover?.();flyout.position()}else panel.hidePopover?.()});menu.addEventListener("keydown",event=>{if(event.key==="Escape"){menu.open=false;summary.focus();event.preventDefault()}});this.syncSelectionControls()},runReportingSelection(action){this.selectionControlBusy=true;this.syncSelectionControls();Promise.resolve().then(action).catch(error=>{if(!this.destroyed&&error.name!=="AbortError"){this.selectionControlError=error.message;this.emit("error",{error})}}).finally(()=>{this.selectionControlBusy=false;if(!this.destroyed){this.syncSelectionControls();if(this.selectionControlError){setText(this.selectionStatus,this.selectionControlError);this.selectionControlError=null}}})},setQuery(input,paging){const pending=SELECTION_CONTROLS_BASE.setQuery.call(this,input,paging);this.syncSelectionControls();return pending},refreshRowState(element,row){SELECTION_CONTROLS_BASE.refreshRowState.call(this,element,row);if(this.view.reporting?.selectionControls&&!row.status)element.setAttribute("aria-selected",String(this.reportingRowSelected(row)))},fillRow(element,row){SELECTION_CONTROLS_BASE.fillRow.call(this,element,row);if(!this.view.reporting?.selectionControls||row.status)return;const cell=element._cells[0].cell;let check=cell.querySelector(".cx-grid-row-select");if(!check){check=h("input","cx-grid-row-select");check.type="checkbox";check.tabIndex=-1;cell.insertBefore(check,cell.firstChild);check.addEventListener("click",event=>{if(!event.shiftKey)return;event.preventDefault();const current=element._w;this.reportingSelectionIntent(current,!this.reportingRowSelected(current),true,event.ctrlKey||event.metaKey)});check.addEventListener("change",()=>{this.reportingSelectionIntent(element._w,check.checked,false,false)});check.addEventListener("keydown",event=>{if(event.key===" "&&event.shiftKey){event.preventDefault();event.stopPropagation();const current=element._w;this.reportingSelectionIntent(current,!this.reportingRowSelected(current),true,event.ctrlKey||event.metaKey)}})}
1249
+ fill(this.labels.gridSelectionCount,{count:format.format(this.selected.size)}));const header=this.headRow?.querySelector(".cx-header-check"),count=query?query.matched===null?null:query.matched-this.selectionExcluded.size:this.selected.size;if(header){header.dataset.cxChecked=count===0?"false":count===this.snapshot?.matched?"true":"mixed";header.setAttribute("aria-label",this.labels.gridSelection+" · "+this.selectionStatus.textContent)}},buildHeaderSelection(){this.headerSelectionFlyout?.dispose();if(!this.selectionControls||!this.headCells[0])return;const menu=h("details","cx-columns cx-header-selection"),summary=h("summary"),panel=h("div","cx-columns-list cx-grid-panel");summary.setAttribute("aria-label",this.labels.gridSelection);summary.classList.add("cx-header-check");summary.setAttribute("aria-haspopup","menu");summary.appendChild(gridIcon("check"));panel.appendChild(this.selectionControls);menu.append(summary,panel);this.headCells[0].prepend(menu);const flyout=this.headerSelectionFlyout=watchFlyout(menu,panel,()=>menu.open);panel.popover="manual";menu.addEventListener("toggle",()=>{if(menu.open){announceMenu(menu);if(panel.isConnected)panel.showPopover?.();flyout.position()}else panel.hidePopover?.()});menu.addEventListener("keydown",event=>{if(event.key==="Escape"){menu.open=false;summary.focus();event.preventDefault()}});this.syncSelectionControls()},runReportingSelection(action){this.selectionControlBusy=true;this.syncSelectionControls();Promise.resolve().then(action).catch(error=>{if(!this.destroyed&&error.name!=="AbortError"){this.selectionControlError=error.message;this.emit("error",{error})}}).finally(()=>{this.selectionControlBusy=false;if(!this.destroyed){this.syncSelectionControls();if(this.selectionControlError){setText(this.selectionStatus,this.selectionControlError);this.selectionControlError=null}}})},setQuery(input,paging){const pending=SELECTION_CONTROLS_BASE.setQuery.call(this,input,paging);this.syncSelectionControls();return pending},refreshRowState(element,row){SELECTION_CONTROLS_BASE.refreshRowState.call(this,element,row);if(this.view.reporting?.selectionControls&&!row.status)element.setAttribute("aria-selected",String(this.reportingRowSelected(row)))},fillRow(element,row){SELECTION_CONTROLS_BASE.fillRow.call(this,element,row);if(!this.view.reporting?.selectionControls||row.status)return;const cell=element._cells[0].cell;let check=cell.querySelector(".cx-grid-row-select");if(!check){check=h("input","cx-grid-row-select");check.type="checkbox";check.tabIndex=-1;cell.insertBefore(check,cell.firstChild);check.addEventListener("click",event=>{if(!event.shiftKey)return;event.preventDefault();const current=element._w;this.reportingSelectionIntent(current,!this.reportingRowSelected(current),true,event.ctrlKey||event.metaKey)});check.addEventListener("change",()=>{this.reportingSelectionIntent(element._w,check.checked,false,false)});check.addEventListener("keydown",event=>{if(event.key===" "&&event.shiftKey){event.preventDefault();event.stopPropagation();const current=element._w;this.reportingSelectionIntent(current,!this.reportingRowSelected(current),true,event.ctrlKey||event.metaKey)}})}
1163
1250
  check.checked=this.reportingRowSelected(row);check.disabled=!!this.pendingQuery;check.setAttribute("aria-label",fill(this.labels.gridSelectRecord,{id:row.key}))},reportingSelectionIntent(row,selected,range,additive){this.runReportingSelection(()=>range&&this.multi&&this.selectionAnchor!=null?
1164
1251
  this.selectRange(this.selectionAnchor,row._ri,{selected,additive}):this.setReportingRowSelected(row,selected))},onClick(event){const element=event.target.closest(".cx-row"),row=element?._w;if(this.view.reporting?.selectionControls&&row&&!row.status&&!event.defaultPrevented&&
1165
1252
  !event.target.closest("a,button,input,select,textarea,[contenteditable]")&&(event.shiftKey||event.ctrlKey||event.metaKey)){this.setFocus(row,true);this.reportingSelectionIntent(row,event.shiftKey||!this.reportingRowSelected(row),event.shiftKey,event.ctrlKey||event.metaKey);return}
@@ -1168,70 +1255,47 @@ catch(error){if(!this.view.reporting?.selectionControls)throw error;this.runRepo
1168
1255
  if(this.view.reporting?.selectionControls&&row&&!row.status&&!event.defaultPrevented&&
1169
1256
  !event.target.closest("a,button,input,select,textarea,[contenteditable]"))this.selectionAnchor=row._ri},onKey(event){const element=event.target.closest(".cx-row"),row=element?._w;if(this.view.reporting?.selectionControls&&row&&!row.status&&event.key===" "&&!event.defaultPrevented&&
1170
1257
  !event.isComposing&&!event.altKey&&!event.target.closest("a,button,input,select,textarea,[contenteditable]")){event.preventDefault();this.reportingSelectionIntent(row,!this.reportingRowSelected(row),event.shiftKey,event.ctrlKey||event.metaKey);return}
1171
- return SELECTION_CONTROLS_BASE.onKey.call(this,event)}});const SUMMARY_VALIDATE=GRID_REPORTING.validate,SUMMARY_COMPARE=GRID_REPORTING.compare,SUMMARY_QUERY=gridQuery;const SUMMARY_BASE={requestPage:DataGrid.prototype.requestPage,render:DataGrid.prototype.render};function reportingSummaryEnabled(view){return!!view.reporting?.grouping||!!view.reporting?.aggregates?.length}
1172
- function reportingGroupKey(value){return JSON.stringify([typeof value,value])}
1173
- function reportingGroupAt(summary,index){let low=0,high=summary.groups.length;while(low<high){const middle=(low+high)>>>1;if(summary.groups[middle].offset<=index)low=middle+1;else high=middle}
1174
- return low?summary.groups[low-1]:null}
1175
- GRID_REPORTING.validate=view=>{SUMMARY_VALIDATE(view);const grouping=view.reporting.grouping,aggregates=view.reporting.aggregates||[],ids=new Set();if(reportingSummaryEnabled(view)&&view.source.navigation==="cursor")throw new Error("CanopyX: summaries require offset navigation.");if(grouping!==undefined&&(!grouping||typeof grouping!=="object"||Array.isArray(grouping)||
1258
+ return SELECTION_CONTROLS_BASE.onKey.call(this,event)}});DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridGroupHeading:"{column}: {value} ({count} records)",gridGroupMissing:"Missing value",gridGroupExpand:"Expand group",gridGroupCollapse:"Collapse group",gridSummary:"Query totals",gridGroupOrder:"Group order is fixed by this report",gridGroupBy:"Group by this column",gridUngroup:"Stop grouping by this column",gridGroupDrop:"Drag a column header here to group rows",gridExpandAll:"Expand all",gridCollapseAll:"Collapse all",gridExpandLevel:"Expand level {level}",gridCollapseLevel:"Collapse level {level}",gridGroupMove:"Alt+Left and Alt+Right move this level",gridGroupSortBy:"Sort groups by",gridGroupSortValue:"by value",gridGroupSortAggregate:"by {aggregate}",gridSelectGroup:"Select the records of {group}",gridAggregateCount:"count {value}",gridAggregateSum:"sum {value}",gridAggregateAverage:"avg {value}",gridAggregateMinimum:"min {value}",gridAggregateMaximum:"max {value}",gridAggregateDistinct:"distinct {value}",gridShowDetail:"Show details",gridHideDetail:"Hide details",gridDetailOf:"Details of {record}",gridDetailFailed:"Details could not load: {error}"}));const GROUP_LEVELS=4,GROUP_OPERATIONS=["count","sum","average","minimum","maximum","distinctCount"];const GROUP_NUMERIC=["sum","average","minimum","maximum"],GRID_ROWS={base:{}};const reportingDynamicGroups=view=>view.reporting?.groupBy!==undefined||!!view.reporting?.groupable;function reportingSummaryEnabled(view){return!!view.reporting?.grouping||reportingDynamicGroups(view)||!!view.reporting?.aggregates?.length}
1259
+ const reportingRowsUsed=grid=>reportingSummaryEnabled(grid.view)||!!grid.view.reporting?.detail||!!grid.opts.detail;function reportingGroupKey(value){return JSON.stringify([typeof value,value])}
1260
+ function reportingGroupLevels(view,input,columns=view.columns){if(!Array.isArray(input)||input.length>GROUP_LEVELS)throw new Error("CanopyX: groupBy lists at most "+GROUP_LEVELS+" levels.");const seen=new Set(),aggregates=view.reporting.aggregates||[];return input.map(level=>{const column=level&&columns.find(item=>item.id===level.column);if(!column||!column.sortable||seen.has(level.column)||!["asc","desc"].includes(level.direction)||
1261
+ Object.keys(level).some(key=>!["column","direction","aggregate"].includes(key))||
1262
+ level.aggregate!==undefined&&!aggregates.some(aggregate=>aggregate.id===level.aggregate))
1263
+ throw new Error("CanopyX: invalid groupBy level.");seen.add(level.column);return gridFreeze({column:level.column,direction:level.direction,...(level.aggregate===undefined?{}:{aggregate:level.aggregate})})})}
1264
+ const GROUPING_VALIDATE=GRID_REPORTING.validate;GRID_REPORTING.validate=view=>{GROUPING_VALIDATE(view);const options=view.reporting,grouping=options.grouping,aggregates=options.aggregates||[],ids=new Set();if(reportingSummaryEnabled(view)&&view.source.navigation==="cursor")throw new Error("CanopyX: summaries require offset navigation.");if(grouping!==undefined&&(!grouping||typeof grouping!=="object"||Array.isArray(grouping)||
1176
1265
  !["asc","desc"].includes(grouping.direction)||
1177
- !view.columns.some(column=>column.id===grouping.column&&column.sortable)))throw new Error("CanopyX: invalid grouping column or order.");if(!Array.isArray(aggregates)||aggregates.length>32)throw new Error("CanopyX: at most 32 aggregates are supported.");for(const aggregate of aggregates){if(!aggregate||typeof aggregate!=="object"||Array.isArray(aggregate))throw new Error("CanopyX: invalid aggregate.");requireIdentifier(aggregate.id,"aggregate ID");const column=view.columns.find(item=>item.id===aggregate.column);if(!column||ids.has(aggregate.id)||!["count","sum","average","minimum","maximum"].includes(aggregate.operation)||
1178
- aggregate.operation!=="count"&&column.kind!=="number"||typeof aggregate.label!=="string"||
1179
- !aggregate.label.trim()||aggregate.label.length>512)throw new Error("CanopyX: invalid aggregate binding.");ids.add(aggregate.id)}};gridQuery=(view,input,strict)=>{const query=SUMMARY_QUERY(view,input,strict),grouping=view.reporting?.grouping;return!grouping?query:gridFreeze({...query,sort:[grouping,...query.sort.filter(sort=>sort.column!==grouping.column)]})};GRID_REPORTING.compare=view=>{const compare=SUMMARY_COMPARE(view),grouping=view.reporting?.grouping;return!grouping?compare:(a,b,column,direction)=>{const compared=compare(a,b,column,direction);if(compared||column.id!==grouping.column)return compared;const left=gridScalar(a.cells[column.id],column),right=gridScalar(b.cells[column.id],column);const result=gridOrder(typeof left,typeof right)||gridOrder(String(left),String(right));return direction==="desc"?-result:result}};function reportingSummaryQuery(view,query,revision){return gridQuery(view,{...query,offset:0,limit:view.pageSize,revision})}
1180
- function reportingSummaryValues(view,values,count){const declarations=view.reporting.aggregates||[];if(!values||typeof values!=="object"||Array.isArray(values)||Object.keys(values).length!==declarations.length)
1181
- throw new Error("CanopyX: summary aggregate bindings differ.");for(const aggregate of declarations){const value=values[aggregate.id];if(!Object.hasOwn(values,aggregate.id)||value!==null&&(typeof value!=="number"||!Number.isFinite(value)||Math.abs(value)>Number.MAX_SAFE_INTEGER)||
1182
- aggregate.operation==="count"&&(!validStructuralCount(value)||value>count)||
1183
- !count&&aggregate.operation!=="count"&&value!==null)throw new Error("CanopyX: invalid aggregate value.")}}
1184
- function reportingSummaryResult(view,query,page,summary){checkDocument(summary,"gridSummary");if(JSON.stringify(summary).length>4194304||summary.view!==view.id||summary.revision!==page.revision||summary.matched!==page.matched||
1185
- !Array.isArray(summary.groups)||summary.groups.length>10000||!validStructuralCount(summary.matched+summary.groups.length)||!view.reporting.grouping&&summary.groups.length||
1186
- !summary.query||!["protocol","type","view","offset","limit","sort","filters","search","revision"].every(key=>Object.hasOwn(summary.query,key))||
1187
- summary.query.type!=="gridQuery"||summary.query.protocol!==PROTOCOL||summary.query.revision!==page.revision||summary.query.offset!==0||
1188
- JSON.stringify(gridQuery(view,summary.query))!==JSON.stringify(query))throw new Error("CanopyX: summary ownership or counts differ.");reportingSummaryValues(view,summary.aggregates,summary.matched);let offset=0;const keys=new Set(),column=view.columns.find(item=>item.id===view.reporting.grouping?.column),values=new Set();const compare=column?GRID_REPORTING.compare(view):null;let previous=null;for(const group of summary.groups){if(!group||!Object.hasOwn(group,"value")||
1189
- group.value!==null&&typeof group.value==="object")throw new Error("CanopyX: groups carry raw values.");requireIdentifier(group.key,"group key");const value=gridScalar(group.value,column),key=reportingGroupKey(value);if(group.cells!==undefined)validateGridRows([{id:group.key,cells:group.cells}],view.columns);if(group.key.length>4096||keys.has(group.key)||values.has(key)||group.offset!==offset||
1190
- !validStructuralCount(group.count)||!group.count||previous&&compare(previous,{cells:{[column.id]:group.value}},column,view.reporting.grouping.direction)>=0)
1191
- throw new Error("CanopyX: invalid complete group range.");previous={cells:{[column.id]:group.value}};keys.add(group.key);values.add(key);offset +=group.count;reportingSummaryValues(view,group.aggregates,group.count)}
1192
- if(view.reporting.grouping&&offset!==page.matched)throw new Error("CanopyX: groups omit matching records.");return gridFreeze(JSON.parse(JSON.stringify(summary)))}
1193
- Object.assign(DataGrid.prototype,{async requestPage(query,signal){const page=await SUMMARY_BASE.requestPage.call(this,query,signal);if(!reportingSummaryEnabled(this.view)||signal!==this.dataLifetime.signal)return page;validateGridPage(page,this.view,query);const request=reportingSummaryQuery(this.view,query,page.revision),key=JSON.stringify(request);if(this.summaryCandidate?.key!==key){const hook=this.source.summary||(this.view.source.kind==="inline"?request=>reportingInlineSummary(this.view,request):null);if(!hook)throw new Error("CanopyX: hosted summaries need an explicit source summary hook.");const summary=await this.runGridHook(signal=>hook.call(this.source,request,{signal}),signal);gridOperationActive(this,signal);this.summaryCandidate={key,result:reportingSummaryResult(this.view,request,page,summary)}}
1194
- if(this.summaryCandidate.result.matched!==page.matched)throw new Error("CanopyX: a page differs from its complete-query summary count.");if(this.view.reporting.grouping){const column=this.view.columns.find(column=>column.id===this.view.reporting.grouping.column);for(let i=0;i<page.items.length;i++){const group=reportingGroupAt(this.summaryCandidate.result,page.offset+i);if(!group||reportingGroupKey(gridScalar(page.items[i].cells[column.id],column))!==reportingGroupKey(gridScalar(group.value,column)))
1195
- throw new Error("CanopyX: a page record differs from its complete-query group.")}}
1196
- return page},render(){if(this.acceptedQuery&&reportingSummaryEnabled(this.view)){const key=JSON.stringify(reportingSummaryQuery(this.view,this.acceptedQuery,this.snapshot.revision));if(this.summaryCandidate?.key===key&&this.summary!==this.summaryCandidate.result){this.summary=this.summaryCandidate.result;this.groupProjectionKey=null}
1197
- this.prepareGroupProjection?.();this.syncSummaryControls?.()}
1198
- return SUMMARY_BASE.render.call(this)}});function reportingAggregateValues(view,rows,first=0,last=rows.length){const values=Object.create(null);for(const aggregate of view.reporting.aggregates||[]){const column=view.columns.find(item=>item.id===aggregate.column);let count=0,sum=0,correction=0,minimum=Infinity,maximum=-Infinity;for(let i=first;i<last;i++){const value=gridScalar(rows[i].cells[column.id],column);if(value===null)continue;count++;if(aggregate.operation!=="count"){const next=value-correction,total=sum+next;correction=total-sum-next;sum=total;minimum=Math.min(minimum,value);maximum=Math.max(maximum,value)}}
1199
- values[aggregate.id]=aggregate.operation==="count"?count:!count?null:
1200
- aggregate.operation==="sum"?sum:aggregate.operation==="average"?sum / count : aggregate.operation === "minimum" ? minimum : maximum;
1201
- }
1202
- return values}
1203
- function reportingInlineSummary(view,query){const columns=new Map(view.columns.map(column=>[column.id,column]));const rows=gridQueryRows(view,query,columns,GRID_REPORTING.compare(view)),groups=[],grouping=view.reporting.grouping;if(grouping){const column=columns.get(grouping.column);for(let offset=0;offset<rows.length;){const value=cellValue(rows[offset].cells[column.id])??null,scalar=gridScalar(value,column),key=reportingGroupKey(scalar);let end=offset+1;while(end<rows.length&&reportingGroupKey(gridScalar(rows[end].cells[column.id],column))===key)end++;const cells=Object.create(null);for(const declared of view.columns){const presenter=declared.presenter||view.reporting.presenters?.[declared.id];if(!presenter||!["state","enum"].includes(presenter.kind))continue;let worst=null,rank=-1;for(let index=offset;index<end;index++){const cell=rows[index].cells[declared.id],raw=cellValue(cell);if(raw==null)continue;const tone=gridPresenterTone(presenter,raw,isCellObject(cell)?cell.tone:null),next=GRID_TONE_RANK[tone]??2;if(next>rank){worst=cell;rank=next}}
1204
- if(worst!==null)cells[declared.id]=worst}
1205
- groups.push({key,value,offset,count:end-offset,aggregates:reportingAggregateValues(view,rows,offset,end),...(Object.keys(cells).length?{cells}:{})});if(groups.length>10000||key.length>4096)throw new Error("CanopyX: inline grouping exceeds its metadata bounds.");offset=end}}
1206
- return{protocol:PROTOCOL,type:"gridSummary",view:view.id,revision:query.revision,query,matched:rows.length,groups,aggregates:reportingAggregateValues(view,rows)}}
1207
- const GROUP_TRIM=DataGrid.prototype.trimInactiveCache;Object.assign(DataGrid.prototype,{renderPass(){return ROW_VIEWPORT.renderPass.call(this,this.rowProjection||this)},trimInactiveCache(){if(this.rowProjection)this.window=this.rowProjection.recordWindow(this.window.first,this.window.last);return GROUP_TRIM.call(this)},prepareGroupProjection(){if(!this.view.reporting.grouping||!this.summary)return;this.collapsedGroups||=new Set();const bounds=this.rowBounds(),key=[this.summary,bounds.first,bounds.last,this.groupCollapseVersion||0];if(this.groupProjectionKey?.every((value,i)=>value===key[i]))return;const segments=[],recordSegments=[];let count=0;for(let i=0;i<this.summary.groups.length;i++){const group=this.summary.groups[i],first=Math.max(bounds.first,group.offset),last=Math.min(bounds.last,group.offset+group.count-1);if(first>last)continue;const header={_ri:-i-1,hpx:this.rowH,h:{cells:{}},status:"group",group};const length=1 +(this.collapsedGroups.has(group.key)?0:last-first+1);const segment={start:count,length,header,first,last};segments.push(segment);recordSegments.push(segment);count +=length}
1208
- const find=(list,index,field)=>{let low=0,high=list.length;while(low<high){const middle=(low+high)>>>1;if(list[middle][field]<=index)low=middle+1;else high=middle}
1209
- return low?list[low-1]:null};const projection={rowCount:()=>count,rowAt:top=>this.autoRows&&this.rowOffsets?this.rowOffsets.at(top):Math.max(0,Math.min(count-1,Math.floor(top / this.rowH))),
1210
- rowTop:index=>this.autoRows&&this.rowOffsets?this.rowOffsets.top(index):index*this.rowH,recordIndex:ordinal=>{const segment=find(recordSegments,ordinal,"first");return!segment||ordinal>segment.last||segment.length===1?-1:segment.start+1+ordinal-segment.first},rowItem:index=>{const segment=find(segments,index,"start");return!segment||index>=segment.start+segment.length?null:index===segment.start?segment.header:this.rowItem(segment.first+index-segment.start-1)},indexOf:row=>{if(!row)return -1;if(row.group){const segment=segments.find(item=>item.header.group.key===row.group.key);return segment?.start??-1}
1211
- const segment=find(recordSegments,row._ri,"first");return!segment||row._ri>segment.last||segment.length===1||this.indexOf(row)<0?-1:segment.start+1+row._ri-segment.first},recordWindow:(first,last)=>{const ranges=[];for(const segment of segments){const start=Math.max(first,segment.start+1),end=Math.min(last,segment.start+segment.length-1);if(start<=end)ranges.push({first:segment.first+start-segment.start-1,last:segment.first+end-segment.start-1})}
1212
- return{first:ranges[0]?.first??bounds.first,last:ranges.at(-1)?.last??-1,ranges}}};const focus=this.focused;if(focus){const segment=focus.group?segments.find(item=>item.header.group.key===focus.group.key):find(recordSegments,focus._ri,"first");if(segment&&(focus.group||segment.length===1))this.focused=segment.header}
1213
- this.rowProjection=projection;this.groupProjectionKey=key;if(this.autoRows)this.syncRowOffsets();else{this.total=count*this.rowH;this.syncScrollRange(this.logicalTop())}
1214
- this.grid.setAttribute("aria-rowcount",String(count+1))},setGroupCollapsed(key,collapsed){if(this.destroyed||this.pendingQuery||!this.summary||!this.view.reporting.grouping||typeof collapsed!=="boolean"||
1215
- !this.summary.groups.some(group=>group.key===key))throw new Error("CanopyX: choose an accepted group and a boolean collapse intent.");this.collapsedGroups||=new Set();const ownedFocus=this.canvas.contains(document.activeElement);if(collapsed&&!this.collapsedGroups.has(key)&&this.collapsedGroups.size>=10000)throw new Error("CanopyX: at most 10,000 collapsed group keys are supported.");if(collapsed)this.collapsedGroups.add(key);else this.collapsedGroups.delete(key);this.groupCollapseVersion=(this.groupCollapseVersion||0)+ 1;this.version++;this.render();if(ownedFocus&&this.focused?.group)this.rendered.get(this.focused)?.focus({preventScroll:true});this.emit("group",{key,collapsed});this.noteViewPreferenceIntent?.();this.stateChanged()}});DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridGroupHeading:"{column}: {value} ({count} records)",gridGroupMissing:"Missing value",gridGroupExpand:"Expand group",gridGroupCollapse:"Collapse group",gridSummary:"Query totals",gridGroupOrder:"Group order is fixed by this report"}));const GROUP_BASE={},GROUP_API=GRID_REPORTING.api;for(const name of["buildGridControls","buildHeader","fillRow","setFocus","onKey","setQuery"])GROUP_BASE[name]=DataGrid.prototype[name];GRID_REPORTING.api=(api,grid)=>{GROUP_API?.(api,grid);if(reportingSummaryEnabled(grid.view))api.getSummary=()=>grid.summary||null;if(grid.view.reporting?.grouping){api.setGroupCollapsed=grid.setGroupCollapsed.bind(grid);api.getCollapsedGroups=()=>[...(grid.collapsedGroups||[])]}};Object.assign(DataGrid.prototype,{buildHeader(){GROUP_BASE.buildHeader.call(this);const column=this.view.reporting?.grouping?.column;const header=this.headCells.find(cell=>cell.getAttribute("data-cx-column")===column),button=header?.querySelector(".cx-sort");if(button){button.disabled=true;button.title=this.labels.gridGroupOrder}},buildGridControls(){GROUP_BASE.buildGridControls.call(this);const grouping=this.view.reporting?.grouping;if(grouping){this.groupBar=h("div","cx-groupbar");const chip=h("span","cx-gchip");setText(chip,this.columns.find(column=>column.id===grouping.column).title);this.groupBar.append(document.createTextNode(this.labels.gridGroupedBy+" "),chip);if(typeof this.opts.onUngroup==="function"){const remove=gridDecorateButton(gridButton("",()=>this.runGridHook(signal=>this.opts.onUngroup({query:this.getQuery(),signal}),this.dataLifetime.signal)
1216
- .catch(error=>this.emit("error",{error}))),"close",true);remove.setAttribute("aria-label",this.labels.gridRemoveGrouping);chip.appendChild(remove)}}
1217
- if(!this.view.reporting?.aggregates?.length)return;this.summaryControls=h("div","cx-grid-summary");this.summaryControls.setAttribute("role","rowgroup");this.summaryControls.setAttribute("aria-label",this.labels.gridSummary);this.toolbar.appendChild(this.summaryControls)},syncSummaryControls(){if(this.statusBar&&this.summary&&this.statusGroupSummary!==this.summary){this.statusGroupSummary=this.summary;this.statusGroups??=h("span");this.statusCount.after(this.statusGroups);this.statusGroups.hidden=!this.summary.groups.length;setText(this.statusGroups,fill(this.labels.gridGroupCount,{count:numberFormat(this.lang).format(this.summary.groups.length)}))}
1218
- if(!this.summaryControls||!this.summary)return;if(this.summaryControls.parentElement!==this.grid)this.grid.appendChild(this.summaryControls);const count=this.rowProjection?this.rowProjection.rowCount():this.rowCount();this.grid.setAttribute("aria-rowcount",String(count+2));const key=[this.summary,count,this.layout,this.rowWidth,...this.cols.map(column=>column.renderWidth+":"+column.effectivePin)];if(this.summaryRenderKey?.every((value,index)=>value===key[index]))return;this.summaryRenderKey=key;this.summaryControls.replaceChildren();const row=h("div","cx-summary-row");row.setAttribute("role","row");row.setAttribute("aria-rowindex",String(count+2));row.style.width=this.rowWidth+"px";this.cols.forEach((column,index)=>{const cell=this.newCell(index,column.width,column.kind);cell.setAttribute("role","gridcell");cell.style.width=column.renderWidth+"px";const aggregates=this.view.reporting.aggregates.filter(item=>item.column===column.id),value=aggregates.map(item=>formatValue(this.summary.aggregates[item.id],"number",this.fmt)).join(" · ");setText(cell,value||index===0?value||this.labels.gridSummary:"");cell.title=aggregates.map(item=>item.label+": "+formatValue(this.summary.aggregates[item.id],"number",this.fmt)).join(" · ");row.appendChild(cell)});this.summaryControls.appendChild(row)},fillRow(element,row){if(!row.group){element.removeAttribute("data-cx-group");return GROUP_BASE.fillRow.call(this,element,row)}
1219
- this.observeDetail(element,false);element.textContent="";element._w=row;element._kind="group";element._layout=this.layout;element._attrs={};element._cls=null;element.className="cx-row cx-grid-group";element.setAttribute("role","row");element.removeAttribute("data-cx-id");element.removeAttribute("data-cx-status");element.removeAttribute("aria-selected");element.removeAttribute("aria-busy");element.dataset.cxGroup=row.group.key;element.style.height=this.rowH+"px";element.style.width=this.rowWidth+"px";const button=h("button","cx-grid-group-toggle");button.type="button";button.tabIndex=-1;const collapsed=this.collapsedGroups.has(row.group.key);button.disabled=!!this.pendingQuery;button.setAttribute("aria-expanded",String(!collapsed));button.setAttribute("aria-label",collapsed?this.labels.gridGroupExpand:this.labels.gridGroupCollapse);setText(button,collapsed?"▸":"▾");button.addEventListener("click",()=>{if(!this.pendingQuery){this.setFocus(row,false);this.setGroupCollapsed(row.group.key,!this.collapsedGroups.has(row.group.key));this.setFocus(this.focused,true)}});const groupColumn=this.columns.find(item=>item.id===this.view.reporting.grouping.column),label=fill(this.labels.gridGroupHeading,{column:groupColumn.title,value:row.group.value===null?this.labels.gridGroupMissing:formatValue(row.group.value,groupColumn.kind,this.fmt),count:numberFormat(this.lang).format(row.group.count)}),cells=[];this.cols.forEach((column,index)=>{const cell=this.cell(element,index,column.width,column.kind);cells.push({cell,content:cell});if(index===0){const text=h("span","cx-grid-group-text"),count=h("span","cx-grid-group-count");setText(text,row.group.value===null?this.labels.gridGroupMissing:formatValue(row.group.value,groupColumn.kind,this.fmt));setText(count,numberFormat(this.lang).format(row.group.count));cell.append(button,text,count);cell.title=label}
1220
- else{const aggregates=(this.view.reporting.aggregates||[]).filter(item=>item.column===column.id);if(!aggregates.length&&Object.hasOwn(row.group.cells||{},column.id)){const options=column.presenter||this.view.reporting.presenters?.[column.id];if(options)gridCellRenderer(this,column,options)({id:row.group.key,cells:row.group.cells},cell);else setText(cell,cellText(row.group.cells[column.id],column,this.fmt))}else{setText(cell,aggregates.map(item=>formatValue(row.group.aggregates[item.id],"number",this.fmt)).join(" · "));cell.title=aggregates.map(item=>item.label+": "+formatValue(row.group.aggregates[item.id],"number",this.fmt)).join(" · ")}}});element._cells=cells;this.paintGridHighlights(element,row)},setQuery(input,paging){const pending=GROUP_BASE.setQuery.call(this,input,paging);const sync=()=>{if(!this.destroyed)for(const button of this.canvas.querySelectorAll(".cx-grid-group-toggle"))button.disabled=!!this.pendingQuery};sync();pending.then(sync,sync);return pending},setFocus(row,focus){if(!this.rowProjection)return GROUP_BASE.setFocus.call(this,row,focus);if(row&&!row.group&&!this.pendingQuery){const group=this.summary?.groups.find(group=>row._ri>=group.offset&&row._ri<group.offset+group.count);if(group&&this.collapsedGroups?.has(group.key))this.setGroupCollapsed(group.key,false)}
1221
- const index=this.rowProjection.indexOf(row);if(index<0||this.destroyed)return;this.pendingNavigation=null;this.focused=row;this.revealGridRow(row,index,this.rowProjection);if(this.render()){this.revealGridRow(row,index,this.rowProjection);this.render()}
1222
- if(focus)this.rendered.get(this.focused)?.focus({preventScroll:true})},onKey(event){const row=event.target.closest(".cx-row")?._w;if(!row||!this.rowProjection||event.defaultPrevented||event.isComposing||event.altKey||event.target.matches(OWN_ARROWS))
1223
- return GROUP_BASE.onKey.call(this,event);const navigation=this.pendingNavigation;if(navigation?.focus&&navigation.epoch===this.queryEpoch&&this.focused===row)
1224
- return GROUP_BASE.onKey.call(this,event);if(this.pendingQuery&&["ArrowUp","ArrowDown","PageUp","PageDown","Home","End"].includes(event.key)){event.preventDefault();return}
1225
- if(row.group&&["ArrowLeft","ArrowRight","Enter"," "].includes(event.key)){if(event.target.closest("button")&&["Enter"," "].includes(event.key))return;event.preventDefault();if(this.pendingQuery)return;this.setGroupCollapsed(row.group.key,event.key==="ArrowLeft"?true:event.key==="ArrowRight"?false:!this.collapsedGroups.has(row.group.key));this.setFocus(this.focused,true);return}
1226
- if(row.group&&["Home","End"].includes(event.key)){event.preventDefault();event.stopPropagation();if(event.ctrlKey||event.metaKey)this.focusResult(event.key==="Home"?0:this.rowCount()- 1,true)
1227
- .catch(error=>{if(error.name!=="AbortError")this.emit("error",{error})});else this.setFocus(row,true);return}
1228
- if(!["ArrowUp","ArrowDown","PageUp","PageDown"].includes(event.key))return GROUP_BASE.onKey.call(this,event);const step=event.key.startsWith("Page")?Math.max(1,Math.floor((this.grid.clientHeight-this.head.offsetHeight)/ this.rowH)) : 1;
1229
- const direction=/Up$/.test(event.key) ? -1 : 1, proposed = this.autoRows && event.key.startsWith("Page") ?
1230
- this.gridPageStep(row,direction,this.rowProjection):this.rowProjection.indexOf(row)+ direction*step;const cells=this.rowCells(this.rendered.get(row)),column=cells.findIndex(cell=>cell.contains(event.target));event.preventDefault();event.stopPropagation();if(this.paged&&(proposed<0||proposed>=this.rowProjection.rowCount())){const bounds=this.rowBounds();if(row.group&&(direction<0&&bounds.first===0||direction>0&&bounds.last===this.rowCount()- 1)){this.setFocus(this.rowProjection.rowItem(Math.max(0,Math.min(this.rowProjection.rowCount()- 1,proposed))),true);return}
1231
- const ordinal=row.group?(direction<0?bounds.first-step:bounds.last+step):row._ri+direction*step;this.focusResult(Math.max(0,Math.min(this.rowCount()- 1,ordinal)),true,column,null)
1232
- .catch(error=>{if(error.name!=="AbortError")this.emit("error",{error})});return}
1233
- const index=Math.max(0,Math.min(this.rowProjection.rowCount()- 1,proposed)),target=this.rowProjection.rowItem(index);if(target?.group)this.setFocus(target,true);else if(target)this.focusResult(target._ri,true,column,null).catch(error=>{if(error.name!=="AbortError")this.emit("error",{error})})}});const GRID_EXPORT_PRESENTATION=new WeakMap();function gridCopyExportPresentation(from,to){const capture=GRID_EXPORT_PRESENTATION.get(from);if(capture)GRID_EXPORT_PRESENTATION.set(to,capture)}
1234
- GRID_REPORTING.captureExport=(grid,request)=>GRID_EXPORT_PRESENTATION.set(request,JSON.parse(JSON.stringify({rules:grid.view.highlightRules||[],columns:grid.view.columns,labels:grid.labels,locale:grid.lang})));GRID_REPORTING.exportColumn=function(grid,column){const policy=grid.columnLayouts?.get(column.id),presenter=column.presenter||grid.view.reporting?.presenters?.[column.id];const minimum=column.minWidth??policy?.minimum??40,maximum=column.maxWidth??policy?.maximum;return{id:column.id,title:column.title,kind:column.kind,width:{unit:"css-px",preferred:column.width,minimum,...(maximum===undefined?{}:{maximum})},wrap:!!column.wrap,alignment:column.kind==="number"?"right":"left",...(presenter?{presenter:JSON.parse(JSON.stringify(presenter))}:{}),...(column.render&&column.hostRenderer!==false?{diagnostics:["custom-renderer"]}:{})}};GRID_REPORTING.exportRecord=function(grid,request,row){const capture=GRID_EXPORT_PRESENTATION.get(request),matches=new Map(),cells=Object.create(null);for(const rule of capture.rules){const key=rule.target==="cell"?rule.column:null;if(!matches.has(key)&&gridMatches(row,rule,capture.columns.find(column=>column.id===rule.column)))matches.set(key,rule)}
1266
+ !view.columns.some(column=>column.id===grouping.column&&column.sortable)))throw new Error("CanopyX: invalid grouping column or order.");if(options.groupable!==undefined&&typeof options.groupable!=="boolean"||grouping!==undefined&&reportingDynamicGroups(view))
1267
+ throw new Error("CanopyX: a fixed grouping cannot be combined with groupBy or groupable.");if(!Array.isArray(aggregates)||aggregates.length>32)throw new Error("CanopyX: at most 32 aggregates are supported.");for(const aggregate of aggregates){if(!aggregate||typeof aggregate!=="object"||Array.isArray(aggregate))throw new Error("CanopyX: invalid aggregate.");requireIdentifier(aggregate.id,"aggregate ID");const column=view.columns.find(item=>item.id===aggregate.column);const custom=!GROUP_OPERATIONS.includes(aggregate.operation);if(!column||ids.has(aggregate.id)||typeof aggregate.operation!=="string"||custom&&!/^[a-z][a-zA-Z\d]{0,31}$/.test(aggregate.operation) ||
1268
+ GROUP_NUMERIC.includes(aggregate.operation)&&column.kind!=="number"||typeof aggregate.label!=="string"||
1269
+ !aggregate.label.trim()||aggregate.label.length>512)throw new Error("CanopyX: invalid aggregate binding.");if(aggregate.format!==undefined)gridNumberFormat(aggregate.format);ids.add(aggregate.id)}
1270
+ if(options.groupBy!==undefined)reportingGroupLevels(view,options.groupBy);const detail=options.detail;if(detail!==undefined&&(!detail||typeof detail!=="object"||Array.isArray(detail)||
1271
+ Object.keys(detail).some(key=>!["template","renderer","view","print","label"].includes(key))||
1272
+ ["template","renderer","view"].filter(key=>detail[key]!==undefined).length>1||
1273
+ detail.template!==undefined&&(typeof detail.template!=="string"||!/^[A-Za-z][\w:.-]{0,255}$/.test(detail.template)) ||
1274
+ detail.renderer!==undefined&&(typeof detail.renderer!=="string"||!/^[a-z][a-z\d-]{0,31}$/.test(detail.renderer)) ||
1275
+ detail.view!==undefined&&(!detail.view||typeof detail.view!=="object"||!["grid","view"].includes(detail.view.type))||
1276
+ detail.print!==undefined&&typeof detail.print!=="boolean"||
1277
+ detail.label!==undefined&&(typeof detail.label!=="string"||!detail.label.trim()||detail.label.length>256)))
1278
+ throw new Error("CanopyX: invalid detail rows definition.")};const GROUPING_QUERY=gridQuery,GROUPING_SHAPE=gridResultShape;gridQuery=(view,input,strict)=>{const grouping=view.reporting?.grouping;if(grouping){const query=GROUPING_QUERY(view,input,strict);return gridFreeze({...query,sort:[grouping,...query.sort.filter(sort=>sort.column!==grouping.column)]})}
1279
+ if(!reportingDynamicGroups(view)||!input||typeof input!=="object")return GROUPING_QUERY(view,input,strict);const{groupBy,...rest}=input,query=GROUPING_QUERY(view,rest,strict);const levels=reportingGroupLevels(view,groupBy===undefined?view.reporting.groupBy||[]:groupBy).map(level=>{const sort=!level.aggregate&&query.sort.find(item=>item.column===level.column);return sort&&sort.direction!==level.direction?gridFreeze({...level,direction:sort.direction}):level});if(!levels.length)return gridFreeze({...query,groupBy:levels});const lead=levels.map(level=>({column:level.column,direction:level.aggregate?"asc":level.direction}));return gridFreeze({...query,groupBy:levels,sort:[...lead,...query.sort.filter(sort=>!levels.some(level=>level.column===sort.column))]})};gridResultShape=query=>query.groupBy?GROUPING_SHAPE(query)+ JSON.stringify(query.groupBy):GROUPING_SHAPE(query);function reportingGroupCompare(compare){const plain=(row,column)=>{const cell=row.cells[column.id];return isCellObject(cell)&&cell.sort!=null?{cells:{[column.id]:{...cell,sort:null}}}:row};return(a,b,column,direction)=>{const compared=compare(plain(a,column),plain(b,column),column,direction);if(compared)return compared;const left=gridScalar(a.cells[column.id],column),right=gridScalar(b.cells[column.id],column);const result=gridOrder(typeof left,typeof right)||gridOrder(String(left),String(right));return direction==="desc"?-result:result}}
1280
+ const GROUPING_COMPARE=GRID_REPORTING.compare;GRID_REPORTING.compare=view=>{const compare=GROUPING_COMPARE(view),grouping=view.reporting?.grouping;if(!grouping)return compare;const group=reportingGroupCompare(compare);return(a,b,column,direction)=>column.id===grouping.column?group(a,b,column,direction):compare(a,b,column,direction)};const GROUPING_INLINE=inlineGridSource;inlineGridSource=view=>{const base=GROUPING_INLINE(view),plain=base.query;if(!reportingDynamicGroups(view))return base;let shape=null,ordered=null;base.query=function(query,options){if(!query.groupBy)return plain.call(base,query,options);if(options.signal.aborted)throw new DOMException("Grid cancelled.","AbortError");if(gridResultShape(query)!==shape){ordered=GRID_ROWS.order(view,query);shape=gridResultShape(query)}
1281
+ return{protocol:PROTOCOL,type:"gridPage",view:view.id,revision:"inline",offset:query.offset,total:view.rows.length,matched:ordered.length,items:ordered.slice(query.offset,query.offset+query.limit)}};return base};const DETAIL_ROWS=definePart(DataGrid,"detail-rows","detail rows",()=>({DataGrid,GRID_ROWS,GRID_REPORTING,h,setText,fill,gridButton,gridFreeze,cellText,formatting,labelsFor,requireIdentifier,GridRowOffsets,TreeGrid,createDataGrid,createTreeGrid,disposeRowHostCells,OWN_ARROWS,hostHtml}));const GROUPING=definePart(DataGrid,"grouping","row grouping",()=>({DataGrid,GRID_ROWS,GRID_REPORTING,h,setText,fill,numberFormat,formatValue,cellValue,cellText,isCellObject,gridScalar,gridQuery,gridFreeze,gridOrder,gridQueryRows,gridButton,gridDecorateButton,gridCellRenderer,gridPresenterTone,GRID_TONE_RANK,validateGridRows,validateGridPage,checkDocument,requireIdentifier,validStructuralCount,PROTOCOL,gridOperationActive,reportingGroupKey,reportingGroupCompare,reportingSummaryEnabled,reportingDynamicGroups,GROUP_OPERATIONS,reportingAggregateParts,disposeRowHostCells,quietControls,TreeGrid}));GRID_ROWS.base.requestPage=DataGrid.prototype.requestPage;Object.assign(DataGrid.prototype,{loadRows(){return this.rowsLoad||=loadPart(reportingSummaryEnabled(this.view)?GROUPING:DETAIL_ROWS).then(()=>{if(!this.destroyed)this.attachRows()},error=>{this.rowsLoad=null;throw error})},async requestPage(query,signal){if(!reportingRowsUsed(this))return GRID_ROWS.base.requestPage.call(this,query,signal);await this.loadRows();return(GRID_ROWS.requestPage||GRID_ROWS.base.requestPage).call(this,query,signal)},setGroupBy(groupBy){if(!reportingDynamicGroups(this.view))throw new Error("CanopyX: this report has no runtime grouping.");const query=this.queryForChange(true),levels=reportingGroupLevels(this.view,groupBy);const removed=(query.groupBy||[]).filter(level=>!levels.some(next=>next.column===level.column)).map(level=>level.column);return this.setQuery(gridQueryStart(query,{groupBy:levels,sort:(query.sort||[]).filter(sort=>!removed.includes(sort.column)&&!levels.some(level=>level.column===sort.column))}))},getGroupBy(){const grouping=this.view.reporting?.grouping;return gridFreeze(grouping?[{column:grouping.column,direction:grouping.direction}]:
1282
+ JSON.parse(JSON.stringify((this.acceptedQuery||gridQuery(this.view,this.view.query||{})).groupBy||[])))}});const GROUPING_API=GRID_REPORTING.api;GRID_REPORTING.api=(api,grid)=>{GROUPING_API?.(api,grid);const later=name=>(...args)=>grid.loadRows().then(()=>grid[name](...args));if(reportingSummaryEnabled(grid.view)){api.getSummary=()=>grid.summary||null;api.getGroupBy=()=>grid.getGroupBy();if(reportingDynamicGroups(grid.view))api.setGroupBy=groupBy=>grid.setGroupBy(groupBy)}
1283
+ if(grid.view.reporting?.grouping||reportingDynamicGroups(grid.view)){api.setGroupCollapsed=(key,collapsed)=>{if(!grid.setGroupCollapsed)throw new Error("CanopyX: choose an accepted group and a boolean collapse intent.");return grid.setGroupCollapsed(key,collapsed)};api.getCollapsedGroups=()=>[...(grid.collapsedGroups||[])];api.expandAllGroups=later("expandAllGroups");api.collapseAllGroups=later("collapseAllGroups");api.setGroupLevelCollapsed=later("setGroupLevelCollapsed")}
1284
+ if(grid.view.reporting?.detail||grid.opts.detail){for(const name of["expandDetail","collapseDetail","toggleDetail"])api[name]=later(name);api.getExpandedDetails=()=>[...(grid.detailRecords?.keys()||[])]}};const GROUP_STATE_BASE={};for(const name of["getState","readGridState","applyStatePresentation","getShareState"])GROUP_STATE_BASE[name]=DataGrid.prototype[name];const reportingGrouped=view=>!!view.reporting?.grouping||reportingDynamicGroups(view);Object.assign(DataGrid.prototype,{getState(){const state=GROUP_STATE_BASE.getState.call(this);if(reportingGrouped(this.view))state.collapsedGroups=[...(this.collapsedGroups||[])];return state},readGridState(input){const query=input?.query,aggregates=this.view.reporting?.aggregates||[],levels=query&&reportingDynamicGroups(this.view)&&Array.isArray(query.groupBy)?
1285
+ query.groupBy.filter(level=>level&&this.columns.some(column=>column.id===level.column&&column.sortable)).slice(0,GROUP_LEVELS)
1286
+ .map(level=>level.aggregate===undefined||aggregates.some(item=>item.id===level.aggregate)?level:(({aggregate,...rest})=>rest)(level)):undefined;const state=GROUP_STATE_BASE.readGridState.call(this,levels?{...input,query:{...query,groupBy:levels}}:input);const groups=input.collapsedGroups===undefined?[]:input.collapsedGroups;if(!Array.isArray(groups)||groups.length>10000||groups.some(key=>typeof key!=="string"||!key.length||key.length>4096))
1287
+ throw new Error("CanopyX: invalid bounded group collapse state.");groups.forEach(key=>requireIdentifier(key,"group key"));if(reportingGrouped(this.view))state.collapsedGroups=[...new Set(groups)];return state},applyStatePresentation(state){if(reportingGrouped(this.view)){this.collapsedGroups=new Set(state.collapsedGroups||[]);this.groupCollapseVersion=(this.groupCollapseVersion||0)+ 1}
1288
+ return GROUP_STATE_BASE.applyStatePresentation.call(this,state)},getShareState(){const{collapsedGroups,...state}=GROUP_STATE_BASE.getShareState.call(this),current=this.getQuery()||this.defaultState.query;if(current.groupBy)state.query=gridQuery(this.view,gridQueryStart({sort:current.sort,searchMode:current.searchMode,groupBy:current.groupBy}));return gridFreeze(state)}});const AGGREGATE_LABELS={count:"gridAggregateCount",sum:"gridAggregateSum",average:"gridAggregateAverage",minimum:"gridAggregateMinimum",maximum:"gridAggregateMaximum",distinctCount:"gridAggregateDistinct"};const reportingCounting=operation=>operation==="count"||operation==="distinctCount";const AGGREGATE_FORMATS=new Map();function reportingAggregateFormat(lang,digits,compact,fixed=true){const key=(lang||"")+ "|"+digits+"|"+compact+"|"+fixed;let format=AGGREGATE_FORMATS.get(key);if(!format){const options=compact?{notation:"compact",minimumFractionDigits:1,maximumFractionDigits:1}:
1289
+ {minimumFractionDigits:fixed?digits:0,maximumFractionDigits:digits};try{format=new Intl.NumberFormat(lang||undefined,{...options,signDisplay:"negative"})}
1290
+ catch(e){try{format=new Intl.NumberFormat(lang||undefined,options)}catch(f){format=new Intl.NumberFormat(undefined,options)}}
1291
+ if(AGGREGATE_FORMATS.size>=32)AGGREGATE_FORMATS.delete(AGGREGATE_FORMATS.keys().next().value);AGGREGATE_FORMATS.set(key,format)}
1292
+ return format}
1293
+ function reportingColumnPrecision(grid,column){if(!grid.aggregatePrecision||grid.aggregatePrecision.summary!==grid.summary)grid.aggregatePrecision={summary:grid.summary,columns:new Map()};const known=grid.aggregatePrecision.columns.get(column.id);if(known!==undefined)return known;let digits=0,numbers=0,visited=0;for(const row of grid.rowCache?.values()||[]){if(++visited>200)break;const value=cellValue(row.h?.cells?.[column.id]);if(typeof value!=="number"||!Number.isFinite(value))continue;numbers++;const text=String(value),dot=text.indexOf(".");if(text.includes("e"))digits=3;else if(dot>=0)digits=Math.max(digits,Math.min(3,text.length-dot-1));if(digits===3)break}
1294
+ if(!numbers)return null;grid.aggregatePrecision.columns.set(column.id,digits);return digits}
1295
+ function reportingAggregateParts(grid,column,aggregates,values){const precision=aggregates.some(item=>!item.format&&!column.format&&!reportingCounting(item.operation))?reportingColumnPrecision(grid,column):0;const text=[],titles=[];for(const aggregate of aggregates){const value=values[aggregate.id];if(value===null||value===undefined)continue;const known=precision!==null,digits=reportingCounting(aggregate.operation)?0:!known?3:aggregate.operation==="average"?Math.min(3,precision+1):precision;const explicit=aggregate.format||(!reportingCounting(aggregate.operation)&&column.format);const shown=explicit?numberText(value,grid.lang,explicit):reportingAggregateFormat(grid.lang,digits,Math.abs(value)>=1e6,known||reportingCounting(aggregate.operation)).format(value);const label=AGGREGATE_LABELS[aggregate.operation];text.push(fill(label?grid.labels[label]:grid.groupAggregates?.get(aggregate.operation)?.label||aggregate.operation+" {value}",{value:shown}));titles.push(aggregate.label+": " +(explicit?shown:reportingAggregateFormat(grid.lang,3,false,false).format(value)))}
1296
+ return{text:text.join(" · "),title:titles.join(" · ")}}
1297
+ const GRID_EXPORT_PRESENTATION=new WeakMap();function gridCopyExportPresentation(from,to){const capture=GRID_EXPORT_PRESENTATION.get(from);if(capture)GRID_EXPORT_PRESENTATION.set(to,capture)}
1298
+ GRID_REPORTING.captureExport=(grid,request)=>GRID_EXPORT_PRESENTATION.set(request,JSON.parse(JSON.stringify({rules:grid.view.highlightRules||[],columns:grid.view.columns,labels:grid.labels,locale:grid.lang})));GRID_REPORTING.exportColumn=function(grid,column){const policy=grid.columnLayouts?.get(column.id),presenter=column.presenter||grid.view.reporting?.presenters?.[column.id];const minimum=column.minWidth??policy?.minimum??40,maximum=column.maxWidth??policy?.maximum;return{id:column.id,title:column.title,kind:column.kind,width:{unit:"css-px",preferred:column.width,minimum,...(maximum===undefined?{}:{maximum})},wrap:!!column.wrap,alignment:column.kind==="number"?"right":"left",...(column.format?{format:{...column.format}}:{}),...(presenter?{presenter:JSON.parse(JSON.stringify(presenter))}:{}),...(column.render&&column.hostRenderer!==false?{diagnostics:["custom-renderer"]}:{})}};GRID_REPORTING.exportRecord=function(grid,request,row){const capture=GRID_EXPORT_PRESENTATION.get(request),matches=new Map(),cells=Object.create(null);for(const rule of capture.rules){const key=rule.target==="cell"?rule.column:null;if(!matches.has(key)&&gridMatches(row,rule,capture.columns.find(column=>column.id===rule.column)))matches.set(key,rule)}
1235
1299
  const fmt=formatting(request.locale||capture.locale,request.timeZone==="local",capture.labels);for(const column of request.columns){const cell=row.cells[column.id],object=isCellObject(cell),value=cellValue(cell)??null,presenter=column.presenter;let text=cellText(cell,column,fmt);const missing=presenter&&(value===null&&(!object||cell.text===undefined)||text==="");const diagnostics=[...(column.diagnostics||[])];if(object&&cell.className)diagnostics.push("custom-class");if(object&&cell.badges?.length)diagnostics.push("badges");if(object&&cell.series)diagnostics.push("history-series");if(missing)text="—";if(!missing&&presenter?.kind==="boolean"&&typeof value==="boolean")text=value?capture.labels.yes:capture.labels.no;if(!missing&&presenter?.kind==="identifier"){const secondary=presenter.secondaryColumn?cellText(row.cells[presenter.secondaryColumn],capture.columns.find(item=>item.id===presenter.secondaryColumn),fmt):object&&cell.secondary;if(secondary)text +="\n"+secondary}
1236
1300
  if(!missing&&presenter?.kind==="change"){const delta=cellValue(row.cells[presenter.deltaColumn]);if(typeof delta==="number"&&Number.isFinite(delta)){text=(delta===0?"":delta>0?"↑ ":"↓ ")+ numberFormat(request.locale||capture.locale).format(Math.abs(delta))+ " "+text;if(delta!==0)diagnostics.push("partial-tone")}}
1237
1301
  let link;if(!missing&&presenter?.kind==="link"){const target=cellValue(presenter.hrefColumn?row.cells[presenter.hrefColumn]:cell);if(typeof target==="string"&&/^[a-z][a-z\d+.-]*:/i.test(target.trim())) {
@@ -1239,9 +1303,7 @@ const safe=gridPresenterLink(target);if(safe&&/^(https?:|mailto:)/i.test(safe))
1239
1303
  else diagnostics.push("unsafe-link")}else if(target)diagnostics.push("relative-link")}
1240
1304
  const mark=matches.get(column.id),tone=mark?.tone??(presenter?.kind==="boolean"&&value===true&&presenter.inverted?"danger":
1241
1305
  presenter?gridPresenterTone(presenter,value,object?cell.tone:null):object?cell.tone:undefined);if(mark?.className)diagnostics.push("custom-class");cells[column.id]={value,text,...(tone?{tone}:{}),...(link?{link}:{}),...(diagnostics.length?{diagnostics:[...new Set(diagnostics)]}:{})}}
1242
- const mark=matches.get(null),tone=mark?.tone??row.tone,diagnostics=row.className||mark?.className?["custom-class"]:[];return gridFreeze({id:row.id,cells,...(tone?{tone}:{}),...(diagnostics.length?{diagnostics}:{})})};const GROUP_STATE_BASE={};for(const name of["getState","readGridState","applyStatePresentation","getShareState"])GROUP_STATE_BASE[name]=DataGrid.prototype[name];Object.assign(DataGrid.prototype,{getState(){const state=GROUP_STATE_BASE.getState.call(this);if(this.view.reporting?.grouping)state.collapsedGroups=[...(this.collapsedGroups||[])];return state},readGridState(input){const state=GROUP_STATE_BASE.readGridState.call(this,input),groups=input.collapsedGroups===undefined?[]:input.collapsedGroups;if(!Array.isArray(groups)||groups.length>10000||groups.some(key=>typeof key!=="string"||!key.length||key.length>4096))
1243
- throw new Error("CanopyX: invalid bounded group collapse state.");groups.forEach(key=>requireIdentifier(key,"group key"));if(this.view.reporting?.grouping)state.collapsedGroups=[...new Set(groups)];return state},applyStatePresentation(state){if(this.view.reporting?.grouping){this.collapsedGroups=new Set(state.collapsedGroups||[]);this.groupCollapseVersion=(this.groupCollapseVersion||0)+ 1}
1244
- return GROUP_STATE_BASE.applyStatePresentation.call(this,state)},getShareState(){const{collapsedGroups,...state}=GROUP_STATE_BASE.getShareState.call(this);return gridFreeze(state)}});const DELIVERY_PREPARE=DataGrid.prototype.prepareExport,DELIVERY_TEXT=DataGrid.prototype.exportText,DELIVERY_API=GRID_REPORTING.api,DELIVERY_CALLBACKS=new WeakMap();function reportingExportTotal(request){return request.recordCount}
1306
+ const mark=matches.get(null),tone=mark?.tone??row.tone,diagnostics=row.className||mark?.className?["custom-class"]:[];return gridFreeze({id:row.id,cells,...(tone?{tone}:{}),...(diagnostics.length?{diagnostics}:{})})};const DELIVERY_PREPARE=DataGrid.prototype.prepareExport,DELIVERY_TEXT=DataGrid.prototype.exportText,DELIVERY_API=GRID_REPORTING.api,DELIVERY_CALLBACKS=new WeakMap();function reportingExportTotal(request){return request.recordCount}
1245
1307
  function reportingFileName(value){if(typeof value!=="string"||!value||value.length>100||value!==value.trim()||
1246
1308
  /[\\/:*?"<>|\x00-\x1f\x7f-\x9f\u2028\u2029\u202a-\u202e\u2066-\u2069]/u.test(value) ||
1247
1309
  /^\.|[. ]$/.test(value) || /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(value))
@@ -1255,22 +1317,24 @@ GRID_REPORTING.api=(api,grid)=>{DELIVERY_API?.(api,grid);api.materializeExport=g
1255
1317
  throw new Error("CanopyX: export locale must be supported by this browser.");try{locale=Intl.getCanonicalLocales(options.locale)[0];if(!Intl.NumberFormat.supportedLocalesOf(locale).length)throw new Error()}catch{throw new Error("CanopyX: export locale must be supported by this browser.")}}
1256
1318
  if(options.timeZone!==undefined&&!["utc","local"].includes(options.timeZone))throw new Error("CanopyX: export time zone is utc or local.");const fileName=reportingFileName(options.fileName??("canopyx-"+this.view.id.replace(/[^a-z0-9_-]/gi, "_")).slice(0, 100));
1257
1319
  const request=gridFreeze({...exported.request,locale,fileName,timeZone:options.timeZone||exported.request.timeZone,columns:exported.request.columns.map(column=>GRID_REPORTING.exportColumn(this,this.columns.find(item=>item.id===column.id)))}),grid=this,signal=options.signal,callback=options.onProgress;GRID_REPORTING.captureExport(this,request);const result=Object.freeze({request,rows:async function*(options){const enumerationSignal=gridExportSignal(signal,options);const progress=reportingExportProgress(grid,request,callback,enumerationSignal);let count=0;progress("reading",0);for await(const row of grid.exportRows(request,enumerationSignal)){yield row;if(++count%100===0)progress("reading",count)}
1258
- progress("generating",count)}});DELIVERY_CALLBACKS.set(result,callback);return result},async exportText(exported,maxRows,delimiter,signal){let count=0;const rows=async function*(){for await(const row of exported.rows()){count++;yield row}};const text=await DELIVERY_TEXT.call(this,{request:exported.request,rows},maxRows,delimiter,signal);reportingExportProgress(this,exported.request,DELIVERY_CALLBACKS.get(exported),signal)("ready",count);return text},materializeExport(options){const exported=this.prepareExport(options),maxRows=options.maxRows??1000,callback=DELIVERY_CALLBACKS.get(exported);return this.runGridHook(async signal=>{const progress=reportingExportProgress(this,exported.request,callback,signal);const result=await reportingExportSnapshot(this,exported,maxRows,signal,progress);progress("ready",result.rows.length);return result},options.signal)},print(options){const exported=this.prepareExport(options),maxRows=options.maxRows??1000,callback=DELIVERY_CALLBACKS.get(exported);if(typeof this.opts.onPrint!=="function")throw new Error("CanopyX: printing needs an onPrint host hook.");return this.runGridHook(async signal=>{const progress=reportingExportProgress(this,exported.request,callback,signal);const snapshot=await reportingExportSnapshot(this,exported,maxRows,signal,progress);await this.opts.onPrint({snapshot,signal});gridOperationActive(this,signal);progress("ready",snapshot.rows.length)},options.signal)},exportToHost(format,options){const exported=this.prepareExport(options),{request}=exported,callback=DELIVERY_CALLBACKS.get(exported);if(typeof format!=="string"||!format.trim()||typeof this.opts.onExport!=="function")throw new Error("CanopyX: host export needs a format and an onExport hook.");return this.runGridHook(async signal=>{const progress=reportingExportProgress(this,request,callback,signal);progress("reading",0);let completed=0;await this.opts.onExport({format,request,capture:exported,signal,reportProgress:(phase,count)=>{completed=progress(phase,count)}});gridOperationActive(this,signal);progress("ready",reportingExportTotal(request)??completed)},options.signal)}});DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridExportProgress:"{completed} of {total} records",gridExportReading:"{completed} records",gridExportCancelled:"Export cancelled",gridCancelExport:"Cancel export"}));const DELIVERY_CONTROLS=DataGrid.prototype.buildExportControls;DataGrid.prototype.buildExportControls=function(){DELIVERY_CONTROLS.call(this);if(!this.view.reporting)return;const panel=this.toolbar.querySelector(".cx-grid-export .cx-grid-panel");if(!panel){if(typeof this.opts.onPrint==="function")this.toolbar.appendChild(Object.assign(gridButton(this.labels.gridPrint,()=>
1259
- Promise.resolve().then(()=>this.print({scope:"filtered"})).catch(error=>this.emit("error",{error}))),{className:"cx-button cx-grid-print"}));return}
1260
- panel.replaceChildren();const scope=h("select");scope.setAttribute("aria-label",this.labels.gridExport);for(const[value,label]of[["filtered","gridFilteredResults"],["selected","gridSelectedRows"],["all","gridAllResults"]])
1261
- scope.appendChild(gridOption(value,this.labels[label]));const status=h("p","cx-grid-delivery-status");status.setAttribute("role","status");const meter=h("progress");meter.setAttribute("aria-label",this.labels.gridExport);meter.hidden=true;let operation=null;const cancel=gridButton(this.labels.gridCancelExport,()=>operation?.abort());cancel.hidden=true;const progress=value=>{if(this.destroyed||!operation||operation.signal.aborted)return;setText(status,fill(value.total===null?this.labels.gridExportReading:this.labels.gridExportProgress,{completed:numberFormat(this.lang).format(value.completed),total:value.total===null?"":numberFormat(this.lang).format(value.total)}));if(value.total===null)meter.removeAttribute("value");else{meter.max=Math.max(1,value.total);meter.value=value.completed}};const run=action=>{if(operation)return;panel.parentElement.open=true;if(!this.phone&&this.overflow)this.overflow.details.open=false;const controller=operation=new AbortController();scope.disabled=true;buttons.forEach(button=>button.disabled=true);cancel.hidden=false;meter.hidden=false;meter.removeAttribute("value");setText(status,this.labels.loading);const options={scope:scope.value,signal:controller.signal,onProgress:progress};Promise.resolve().then(()=>action(options)).then(()=>{if(!this.destroyed)setText(status,"")},error=>{if(!this.destroyed)setText(status,error.name==="AbortError"?this.labels.gridExportCancelled:error.message);if(error.name!=="AbortError")this.emit("error",{error})}).finally(()=>{if(operation===controller)operation=null;if(!this.destroyed){const returnFocus=document.activeElement===cancel;scope.disabled=false;buttons.forEach(button=>button.disabled=false);cancel.hidden=true;meter.hidden=true;if(returnFocus)scope.focus()}})};const csv=gridButton(this.labels.gridDownloadCsv,()=>run(async options=>{const exported=this.prepareExport(options),fileName=exported.request.fileName;const text=await this.exportText(exported,50000,",",options.signal);gridOperationActive(this,options.signal);const url=URL.createObjectURL(new Blob([text],{type:"text/csv;charset=utf-8"}));try{const link=h("a");link.href=url;link.download=fileName+".csv";document.body.appendChild(link);link.click();link.remove()}finally{setTimeout(()=>URL.revokeObjectURL(url),1000)}}));const copy=gridButton(this.labels.gridCopy,()=>run(options=>this.copy(options))),buttons=[csv,copy];if(typeof this.opts.onPrint==="function"){const print=gridButton(this.labels.gridPrint,()=>run(options=>this.print(options)));print.classList.add("cx-grid-print");this.toolbar.appendChild(print);buttons.push(print)}
1262
- panel.append(scope,...buttons,cancel,meter,status)};const TOOLBAR_CHROME=DataGrid.prototype.buildGridChrome,TOOLBAR_DESTROY=DataGrid.prototype.destroy,TOOLBAR_ACTION=DataGrid.prototype.gridAction,TOOLBAR_API=GRID_REPORTING.api;Object.assign(DataGrid.prototype,{buildGridChrome(){TOOLBAR_CHROME.call(this);const first=this.toolbar.firstChild,search=this.toolbar.querySelector(".cx-search");this.slots={__proto__:null};for(const[name,before]of[["start",first],["afterSearch",search?search.nextSibling:first],["afterViews",this.spacer],["end",this.selectionBar]]){const slot=this.slots[name]=h("div","cx-toolbar-slot");slot.dataset.cxToolbarSlot=name;before.before(slot)}
1320
+ progress("generating",count)}});DELIVERY_CALLBACKS.set(result,callback);return result},async exportText(exported,maxRows,delimiter,signal){let count=0;const rows=async function*(){for await(const row of exported.rows()){count++;yield row}};const text=await DELIVERY_TEXT.call(this,{request:exported.request,rows},maxRows,delimiter,signal);reportingExportProgress(this,exported.request,DELIVERY_CALLBACKS.get(exported),signal)("ready",count);return text},materializeExport(options){const exported=this.prepareExport(options),maxRows=options.maxRows??1000,callback=DELIVERY_CALLBACKS.get(exported);return this.runGridHook(async signal=>{const progress=reportingExportProgress(this,exported.request,callback,signal);const result=await reportingExportSnapshot(this,exported,maxRows,signal,progress);progress("ready",result.rows.length);return result},options.signal)},print(options){const exported=this.prepareExport(options),maxRows=options.maxRows??1000,callback=DELIVERY_CALLBACKS.get(exported);if(typeof this.opts.onPrint!=="function")throw new Error("CanopyX: printing needs an onPrint host hook.");return this.runGridHook(async signal=>{const progress=reportingExportProgress(this,exported.request,callback,signal);const snapshot=await reportingExportSnapshot(this,exported,maxRows,signal,progress);await this.opts.onPrint({snapshot,signal,layout:this.layoutMode==="cards"?"stacked":"table"});gridOperationActive(this,signal);progress("ready",snapshot.rows.length)},options.signal)},exportToHost(format,options){const exported=this.prepareExport(options),{request}=exported,callback=DELIVERY_CALLBACKS.get(exported);if(typeof format!=="string"||!format.trim()||typeof this.opts.onExport!=="function")throw new Error("CanopyX: host export needs a format and an onExport hook.");return this.runGridHook(async signal=>{const progress=reportingExportProgress(this,request,callback,signal);progress("reading",0);let completed=0;await this.opts.onExport({format,request,capture:exported,signal,reportProgress:(phase,count)=>{completed=progress(phase,count)}});gridOperationActive(this,signal);progress("ready",reportingExportTotal(request)??completed)},options.signal,exportTimeout(this,options))}});const DELIVERY_CONTROLS=DataGrid.prototype.buildExportControls,PRINT_CHROME=DataGrid.prototype.buildGridChrome;DataGrid.prototype.exportFromMenu=function(action){return loadExportPart().then(()=>EXPORT_IMPL.exportFromMenu.call(this,action))};DataGrid.prototype.syncPrintAvailability=function(){if(this.printButton)this.printButton.disabled=!!this.exportControl?.panel?.cxExportBusy?.()||!this.defaultExportColumns().length};DataGrid.prototype.buildGridChrome=function(){PRINT_CHROME.call(this);const menu=this.overflow?.details;if(menu&&this.printButton)menu.addEventListener("toggle",()=>{if(menu.open)this.syncPrintAvailability()},{signal:this.lifetime.signal});this.syncPrintAvailability()};DataGrid.prototype.buildExportControls=function(){DELIVERY_CONTROLS.call(this);if(!this.view.reporting||typeof this.opts.onPrint!=="function")return;const print=this.printButton=gridButton(this.labels.gridPrint,()=>Promise.resolve().then(()=>this.exportControl?
1321
+ this.exportFromMenu((options,onProgress)=>this.print({...options,onProgress})):this.print({scope:"filtered"})).catch(error=>this.emit("error",{error})));print.classList.add("cx-grid-print");this.toolbar.appendChild(print)};const TOOLBAR_CHROME=DataGrid.prototype.buildGridChrome,TOOLBAR_DESTROY=DataGrid.prototype.destroy,TOOLBAR_ACTION=DataGrid.prototype.gridAction,TOOLBAR_API=GRID_REPORTING.api;Object.assign(DataGrid.prototype,{buildGridChrome(){TOOLBAR_CHROME.call(this);const first=this.toolbar.firstChild,search=this.toolbar.querySelector(".cx-search");this.slots={__proto__:null};for(const[name,before]of[["start",first],["afterSearch",search?search.nextSibling:first],["afterViews",this.spacer],["end",this.selectionBar]]){const slot=this.slots[name]=h("div","cx-toolbar-slot");slot.dataset.cxToolbarSlot=name;before.before(slot)}
1263
1322
  this.slotted=[...this.host.querySelectorAll(":scope > [data-cx-slot]")];this.slotted.map(node=>this.toolbarSlot(node.dataset.cxSlot)).forEach((slot,index)=>slot.appendChild(this.slotted[index]))},toolbarSlot(name){const slot=this.slots[name];if(!slot)throw new Error("CanopyX: toolbar slots are start, afterSearch, afterViews and end.");return slot},destroy(){if(!this.destroyed&&this.slotted)this.host.append(...this.slotted.filter(node=>this.toolbar.contains(node)));return TOOLBAR_DESTROY.call(this)},gridAction(action,act){const render=action?.render;if(render===undefined)return TOOLBAR_ACTION.call(this,action,act);if(typeof action.label!=="string"||typeof render!=="function"||action.run!==undefined)
1264
1323
  throw new Error("CanopyX: an action has a label and either a run or a render function.");const entry=h("details","cx-grid-action"),summary=h("summary","cx-button"),body=h("div","cx-grid-action-body"),menu=this.overflow.details;setText(summary,action.label);summary.setAttribute("role","button");entry.append(summary,body);entry.addEventListener("toggle",()=>{if(!entry.open)return;if(!body.drawn){body.drawn=true;try{render(body,{close:()=>{entry.open=false;menu.open=false;menu.firstChild.focus()},signal:this.lifetime.signal})}
1265
1324
  catch(error){body.drawn=false;body.replaceChildren();this.emit("error",{error})}}
1266
1325
  (body.querySelector(":is(input:not([type=hidden]),select,textarea,button):not(:disabled),[href],[tabindex]:not([tabindex=\"-1\"])")||summary).focus()});entry.addEventListener("keydown",event=>{if(event.key==="Escape"&&entry.open){entry.open=false;summary.focus();event.preventDefault();event.stopPropagation()}});return entry}});GRID_REPORTING.api=(api,grid)=>{TOOLBAR_API(api,grid);api.toolbarSlot=name=>grid.toolbarSlot(name)};function createTreeGrid(host,options){if(!host||!options||!options.view){throw new Error("CanopyX: createTreeGrid needs a host element and a view document.")}
1267
- const existing=activeGrids.get(host);if(existing)return existing;const grid=new TreeGrid(host,options);const api=Object.freeze(Object.assign({element:host,view:grid.view,ready:grid.ready,expandPath:(ids,o)=>grid.expandPath(ids,o),setFilter:(filter,o)=>{if(!grid.queryAware)grid.clearDeclaredFilters();grid.setFilter(filter,o)},setFilterActive:(id,active)=>grid.setFilterActive(id,active),setActiveFilters:(ids)=>grid.setActiveFilters(ids),updateActiveFilters:(change)=>grid.updateActiveFilters(change),getActiveFilters:()=>Array.from(grid.activeFilters),sort:(columnId,direction)=>grid.sort(columnId,direction),getQuery:()=>grid.getQuery(),setQuery:(query,options)=>grid.setQuery(query,options),setColumnVisible:(columnId,visible)=>grid.setColumnVisible(columnId,visible),setColumnWidth:(columnId,width)=>grid.setColumnWidth(columnId,width),setTreeColumnPinned:(pinned)=>grid.setTreeColumnPinned(pinned),setTimeZone:(zone)=>grid.setTimeZone(zone),getTimeZone:()=>(grid.local?"local":"utc"),refresh:()=>{grid.version++;grid.scheduleRender()},reload:(id,options)=>grid.reload(id,options),readLocation:()=>grid.readLocation(),getSelected:()=>Array.from(grid.selected).map((w)=>w.h),setSelection:(ids)=>grid.setSelection(ids),clearSelection:()=>grid.setSelection([]),selectAll:(options)=>grid.selectAll(options),focus:()=>{if(grid.destroyed)return;const target=grid.indexOf(grid.focused)>=0?grid.focused:grid.rows[0];if(target)grid.setFocus(target,true);else grid.grid.focus({preventScroll:true})},visibleRows:()=>grid.visibleRows(),toOutline:()=>grid.toOutline(),getState:()=>grid.getState(),setState:(state)=>grid.setState(state),search:(query,limit)=>grid.search(query,limit||(grid.view.search&&grid.view.search.maxResults)||20),loadMore:(id)=>{const w=id==null?grid.root:grid.keyMap.get(id);return w?grid.loadMore(w):Promise.resolve()},applyCounts:(doc)=>grid.applyCounts(doc),refreshCounts:(ids)=>grid.refreshCounts(ids),destroy:()=>{grid.destroy();if(activeGrids.get(host)===api)activeGrids.delete(host);if(host.canopyx===api)delete host.canopyx}},grid.interactionApi&&grid.interactionApi()));activeGrids.set(host,api);host.setAttribute("data-canopyx-mounted","true");host.canopyx=api;return api}
1326
+ const existing=activeGrids.get(host);if(existing)return existing;const grid=new TreeGrid(host,options);const api=Object.assign({element:host,view:grid.view,ready:startPlugins(grid,()=>api),expandPath:(ids,o)=>grid.expandPath(ids,o),setFilter:(filter,o)=>{if(!grid.queryAware)grid.clearDeclaredFilters();grid.setFilter(filter,o)},setFilterActive:(id,active)=>grid.setFilterActive(id,active),setActiveFilters:(ids)=>grid.setActiveFilters(ids),updateActiveFilters:(change)=>grid.updateActiveFilters(change),getActiveFilters:()=>Array.from(grid.activeFilters),sort:(columnId,direction)=>grid.sort(columnId,direction),getQuery:()=>grid.getQuery(),setQuery:(query,options)=>grid.setQuery(query,options),setColumnVisible:(columnId,visible)=>grid.setColumnVisible(columnId,visible),setColumnWidth:(columnId,width)=>grid.setColumnWidth(columnId,width),setTreeColumnPinned:(pinned)=>grid.setTreeColumnPinned(pinned),setTimeZone:(zone)=>grid.setTimeZone(zone),getTimeZone:()=>(grid.local?"local":"utc"),refresh:()=>{grid.version++;grid.scheduleRender()},reload:(id,options)=>grid.reload(id,options),readLocation:()=>grid.readLocation(),getSelected:()=>Array.from(grid.selected).map((w)=>w.h),setSelection:(ids)=>grid.setSelection(ids),clearSelection:()=>grid.setSelection([]),selectAll:(options)=>grid.selectAll(options),focus:()=>{if(grid.destroyed)return;const target=grid.indexOf(grid.focused)>=0?grid.focused:grid.rows[0];if(target)grid.setFocus(target,true);else grid.grid.focus({preventScroll:true})},visibleRows:()=>grid.visibleRows(),toOutline:()=>grid.toOutline(),getState:()=>grid.getState(),setState:(state)=>grid.setState(state),getLayout:()=>grid.getLayout(),setLayout:(change)=>grid.setLayout(change),getMatches:()=>grid.getMatches(),setSearchHighlight:(enabled)=>grid.setSearchHighlight(enabled),nextMatch:(options)=>grid.nextMatch(options),previousMatch:(options)=>grid.previousMatch(options),search:(query,limit)=>grid.search(query,limit||(grid.view.search&&grid.view.search.maxResults)||20),loadMore:(id)=>{const w=id==null?grid.root:grid.keyMap.get(id);return w?grid.loadMore(w):Promise.resolve()},applyCounts:(doc)=>grid.applyCounts(doc),refreshCounts:(ids)=>grid.refreshCounts(ids),closeMenus:()=>grid.closeMenus?.(),destroy:()=>{grid.destroy();if(activeGrids.get(host)===api)activeGrids.delete(host);if(host.canopyx===api)delete host.canopyx}},grid.interactionApi&&grid.interactionApi(),linkApi(grid));for(const name of["prepareExport","exportAs","getSavedViews","saveView","applySavedView","removeSavedView","resetView","setColumnOrder","resetColumns","setColumnPinned","openDetails","openRecord"])
1327
+ api[name]=(...args)=>{if(grid[name])return grid[name](...args);const error=new Error("CanopyX: "+name+" needs the tree tools part.");if(/^(exportAs|applySavedView|resetView|openDetails|openRecord)$/.test(name)) return Promise.reject(error);
1328
+ throw error};api.ready=linkReady(grid,api.ready);Object.freeze(api);activeGrids.set(host,api);host.setAttribute("data-canopyx-mounted","true");host.canopyx=api;recordStart(grid);return api}
1268
1329
  const configured=new Map();function configure(viewId,options){requireIdentifier(viewId,"view ID");if(!options||typeof options!=="object"){throw new Error("CanopyX: configure needs a view id and an options object.")}
1269
1330
  if(Array.from(document.querySelectorAll("[data-canopyx-mounted]")).some((host)=>host.canopyx&&host.canopyx.view.id===viewId)){throw new Error("CanopyX: view '"+viewId+"' is already mounted; configure it before the page has loaded, or mark its fragment data-cx-mount=\"manual\" and call CanopyX.mount.")}
1270
1331
  const registration=Object.assign({},configured.get(viewId),options,{view:undefined});configured.set(viewId,registration);return()=>{if(configured.get(viewId)===registration)configured.delete(viewId)}}
1271
1332
  function unconfigure(viewId){requireIdentifier(viewId,"view ID");return configured.delete(viewId)}
1333
+ const parsedViews=new WeakSet();let watcher=null;function mountWhenVisible(host){if(!watcher){watcher=new IntersectionObserver(entries=>entries.forEach(entry=>entry.isIntersecting&&mount(entry.target)),{rootMargin:Math.ceil(global.innerHeight / 2) + "px 0px" });
1334
+ global.addEventListener("beforeprint",()=>document.querySelectorAll('[data-cx-mount="visible"]:not([data-canopyx-mounted])').forEach(host=>mount(host)))}
1335
+ watcher.observe(host)}
1272
1336
  function mount(root,automatic){const scope=root||document;const created=[];const showError=(host,err,api,view)=>{if(api){api.destroy();if(host.canopyx===api)delete host.canopyx}
1273
1337
  const message=err&&err.message?err.message:String(err);let labels=DEFAULT_LABELS;for(const attempt of[()=>labelsFor(view&&view.labels,view&&configured.has(view.id)?configured.get(view.id).labels:null),()=>labelsFor(view&&view.labels)]){try{labels=attempt();break}catch(e){labels=DEFAULT_LABELS}}
1274
- const notice=document.createElement("div");notice.className="cx-mount-error";notice.setAttribute("role","alert");setText(notice,fill(labels.mountError,{error:message}));host.classList.add("cx-host");host.setAttribute("data-canopyx-mounted","true");host.setAttribute("data-cx-error",message);host.appendChild(notice);if(global.console)global.console.error(err)};scope.querySelectorAll("[data-canopyx-host]:not([data-canopyx-mounted])").forEach((host)=>{const config=host.querySelector("script.cx-config");if(!config||(automatic===true&&host.getAttribute("data-cx-mount")==="manual")){return}
1275
- host.setAttribute("data-canopyx-mounted","true");let view=null;try{view=JSON.parse(config.textContent);const create=view&&view.type==="grid"&&typeof createDataGrid==="function"?createDataGrid:createTreeGrid;const api=create(host,Object.assign({},view&&configured.get(view.id),{view}));created.push(api);api.ready.catch((err)=>{if(view.type!=="grid"&&activeGrids.get(host)===api&&host.canopyx===api)showError(host,err,api,view)})}catch(err){showError(host,err,null,view)}});return created}
1276
- global.CanopyX=Object.freeze(Object.assign({protocol:PROTOCOL,createTreeGrid,configure,unconfigure,mount:(root)=>mount(root,false),sources:Object.freeze({inline:inlineSource,sidecar:sidecarSource,remote:remoteSource}),badge:hostBadge,labels:DEFAULT_LABELS},typeof createDataGrid==="function"?{createDataGrid}:{}));const navigation=global.performance&&performance.getEntriesByType?performance.getEntriesByType("navigation")[0]:null;if(document.readyState==="loading"||(document.readyState==="interactive"&&navigation&&navigation.domContentLoadedEventStart===0)){document.addEventListener("DOMContentLoaded",()=>mount(document,true))}else{mount(document,true)}})(typeof window!=="undefined"?window:this);
1338
+ const notice=document.createElement("div");notice.className="cx-mount-error";notice.setAttribute("role","alert");setText(notice,fill(labels.mountError,{error:message}));host.classList.add("cx-host");host.setAttribute("data-canopyx-mounted","true");host.setAttribute("data-cx-error",message);host.appendChild(notice);if(global.console)global.console.error(err)};const selector="[data-canopyx-host]:not([data-canopyx-mounted])";(scope.matches?.(selector)?[scope]:scope.querySelectorAll(selector)).forEach((host)=>{const config=host.querySelector("script.cx-config"),mode=host.getAttribute("data-cx-mount");if(!config||(automatic===true&&mode==="manual")){return}
1339
+ if(mode==="visible"&&host!==scope&&global.IntersectionObserver)return mountWhenVisible(host);watcher?.unobserve(host);host.setAttribute("data-canopyx-mounted","true");let view=null;try{view=JSON.parse(config.textContent);if(view===Object(view))parsedViews.add(view);const create=view&&view.type==="grid"&&typeof createDataGrid==="function"?createDataGrid:createTreeGrid;const api=create(host,Object.assign({},view&&configured.get(view.id),{view}));created.push(api);api.ready.catch((err)=>{if(view.type!=="grid"&&activeGrids.get(host)===api&&host.canopyx===api)showError(host,err,api,view)})}catch(err){showError(host,err,null,view)}});return created}
1340
+ global.CanopyX=Object.freeze(Object.assign({protocol:PROTOCOL,createTreeGrid,configure,unconfigure,mount:(root)=>mount(root,false),sources:Object.freeze({inline:inlineSource,sidecar:sidecarSource,remote:remoteSource}),badge:hostBadge,labels:DEFAULT_LABELS,registerExportFormat,use,registerPart,setNonce},typeof GRID_API==="object"?GRID_API:{}));const navigation=global.performance&&performance.getEntriesByType?performance.getEntriesByType("navigation")[0]:null;if(document.readyState==="loading"||(document.readyState==="interactive"&&navigation&&navigation.domContentLoadedEventStart===0)){document.addEventListener("DOMContentLoaded",()=>mount(document,true))}else{mount(document,true)}})(typeof window!=="undefined"?window:this);