@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.
- package/CanopyX.Html/Assets/canopyx-calendar-edit.js +114 -0
- package/CanopyX.Html/Assets/canopyx-calendar-ics.js +87 -0
- package/CanopyX.Html/Assets/canopyx-calendar-recurrence.js +36 -0
- package/CanopyX.Html/Assets/canopyx-calendar.css +279 -0
- package/CanopyX.Html/Assets/canopyx-calendar.js +307 -0
- package/CanopyX.Html/Assets/canopyx-cell-range.css +38 -0
- package/CanopyX.Html/Assets/canopyx-cell-range.js +141 -0
- package/CanopyX.Html/Assets/canopyx-column-groups.css +18 -0
- package/CanopyX.Html/Assets/canopyx-column-groups.js +51 -0
- package/CanopyX.Html/Assets/canopyx-data.js +14 -0
- package/CanopyX.Html/Assets/canopyx-detail-rows.css +18 -0
- package/CanopyX.Html/Assets/canopyx-detail-rows.js +51 -0
- package/CanopyX.Html/Assets/canopyx-editing.css +44 -0
- package/CanopyX.Html/Assets/canopyx-editing.js +134 -0
- package/CanopyX.Html/Assets/canopyx-enhance.js +126 -0
- package/CanopyX.Html/Assets/canopyx-export.js +18 -0
- package/CanopyX.Html/Assets/canopyx-facets.css +86 -0
- package/CanopyX.Html/Assets/canopyx-facets.js +65 -0
- package/CanopyX.Html/Assets/canopyx-filter-editor.css +15 -18
- package/CanopyX.Html/Assets/canopyx-filter-editor.js +23 -17
- package/CanopyX.Html/Assets/canopyx-format-editor.css +77 -0
- package/CanopyX.Html/Assets/canopyx-format-editor.js +30 -0
- package/CanopyX.Html/Assets/canopyx-formatting.css +40 -0
- package/CanopyX.Html/Assets/canopyx-formatting.js +101 -0
- package/CanopyX.Html/Assets/canopyx-grid-module.mjs +352 -246
- package/CanopyX.Html/Assets/canopyx-grid.css +139 -117
- package/CanopyX.Html/Assets/canopyx-grid.js +352 -246
- package/CanopyX.Html/Assets/canopyx-grouping.css +39 -0
- package/CanopyX.Html/Assets/canopyx-grouping.js +85 -0
- package/CanopyX.Html/Assets/canopyx-highlight.css +34 -0
- package/CanopyX.Html/Assets/canopyx-highlight.js +80 -0
- package/CanopyX.Html/Assets/canopyx-history.js +13 -0
- package/CanopyX.Html/Assets/canopyx-interactive.css +18 -6
- package/CanopyX.Html/Assets/canopyx-interactive.js +179 -100
- package/CanopyX.Html/Assets/canopyx-labels-de.js +304 -0
- package/CanopyX.Html/Assets/canopyx-labels-es.js +304 -0
- package/CanopyX.Html/Assets/canopyx-labels-fr.js +529 -0
- package/CanopyX.Html/Assets/canopyx-labels-it.js +529 -0
- package/CanopyX.Html/Assets/canopyx-labels-pl.js +304 -0
- package/CanopyX.Html/Assets/canopyx-layouts.css +48 -0
- package/CanopyX.Html/Assets/canopyx-layouts.js +39 -0
- package/CanopyX.Html/Assets/canopyx-link.css +19 -0
- package/CanopyX.Html/Assets/canopyx-link.js +38 -0
- package/CanopyX.Html/Assets/canopyx-links.js +14 -0
- package/CanopyX.Html/Assets/canopyx-live.css +24 -0
- package/CanopyX.Html/Assets/canopyx-live.js +88 -0
- package/CanopyX.Html/Assets/canopyx-module.mjs +180 -100
- package/CanopyX.Html/Assets/canopyx-pager.css +10 -0
- package/CanopyX.Html/Assets/canopyx-pager.js +14 -0
- package/CanopyX.Html/Assets/canopyx-record-panel.css +111 -0
- package/CanopyX.Html/Assets/canopyx-record-panel.js +178 -0
- package/CanopyX.Html/Assets/canopyx-reporting-module.mjs +406 -342
- package/CanopyX.Html/Assets/canopyx-reporting.css +140 -135
- package/CanopyX.Html/Assets/canopyx-reporting.js +406 -342
- package/CanopyX.Html/Assets/canopyx-search.js +32 -0
- package/CanopyX.Html/Assets/canopyx-timeline.css +80 -0
- package/CanopyX.Html/Assets/canopyx-timeline.js +88 -0
- package/CanopyX.Html/Assets/canopyx-tree-tools.css +103 -0
- package/CanopyX.Html/Assets/canopyx-tree-tools.js +92 -0
- package/CanopyX.Html/Assets/canopyx.css +17 -5
- package/CanopyX.Html/Assets/canopyx.js +175 -96
- package/JavaScript/calendar.d.ts +362 -0
- package/JavaScript/cell-ranges.d.ts +76 -0
- package/JavaScript/column-groups.d.ts +32 -0
- package/JavaScript/editing.d.ts +257 -0
- package/JavaScript/enhance.d.ts +202 -0
- package/JavaScript/facets.d.ts +58 -0
- package/JavaScript/formatting.d.ts +88 -0
- package/JavaScript/grid.d.ts +409 -56
- package/JavaScript/hosts.d.ts +177 -2
- package/JavaScript/index.d.ts +53 -4
- package/JavaScript/live.d.ts +115 -0
- package/JavaScript/locales/de.mjs +304 -0
- package/JavaScript/locales/es.mjs +304 -0
- package/JavaScript/locales/fr.d.ts +6 -0
- package/JavaScript/locales/fr.mjs +527 -0
- package/JavaScript/locales/it.d.ts +6 -0
- package/JavaScript/locales/it.mjs +527 -0
- package/JavaScript/locales/pl.mjs +304 -0
- package/JavaScript/plugins.d.ts +113 -0
- package/JavaScript/protocol.d.ts +139 -6
- package/JavaScript/records.d.ts +136 -0
- package/README.md +61 -9
- package/README.nuget.md +1 -1
- package/package.json +99 -3
|
@@ -5,6 +5,7 @@ if(typeof document!=="undefined")runtimeHost.CanopyXFilterEditorBase=new URL("./
|
|
|
5
5
|
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}
|
|
6
6
|
return element}
|
|
7
7
|
function setText(element,value){element.textContent=value==null?"":String(value)}
|
|
8
|
+
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)}
|
|
8
9
|
function validStructuralCount(value){return Number.isInteger(value)&&value>=0&&value<=MAX_PROTOCOL_INTEGER}
|
|
9
10
|
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.")}}
|
|
10
11
|
function checkDocument(doc,type){if(!doc||typeof doc!=="object"){throw new Error("CanopyX: expected a "+type+" document, got "+typeof doc)}
|
|
@@ -33,29 +34,21 @@ return tone}
|
|
|
33
34
|
function applyTone(element,base,tone){element.className=base+tone.cls;toneColors(element,tone)}
|
|
34
35
|
function toneColors(element,tone){if((element._tone||null)===tone.host){return}
|
|
35
36
|
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")}}
|
|
36
|
-
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})}
|
|
37
|
-
if(numberFormats.size>=16)numberFormats.delete(numberFormats.keys().next().value);numberFormats.set(key,format)}
|
|
38
|
-
return format}
|
|
39
37
|
function pad2(value){return value<10?"0"+value:String(value)}
|
|
40
38
|
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)}
|
|
41
39
|
return texts}
|
|
42
|
-
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.");
|
|
43
|
-
if((name==="utcTime"||name==="localTime")&&source[name].indexOf("{time}")<0)throw new Error("CanopyX: label '"+name+"' must hold {time}.");labels[name]=source[name]})}
|
|
44
|
-
return Object.freeze(labels)}
|
|
45
|
-
function fill(template,values){return template.replace(/\{(\w+)\}/g, (match, name) => (values && Object.hasOwn(values, name) ? String(values[name]) : match));
|
|
46
|
-
}
|
|
47
40
|
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)}}
|
|
48
41
|
function formatDate(value,fmt){const texts=dateText(value);return texts?(fmt.local?texts[1]:fmt.utc[0]+ texts[0]+ fmt.utc[1]):String(value)}
|
|
49
42
|
function otherZone(cell,column,fmt){const value=cellValue(cell);if(column.kind!=="datetime"||typeof value!=="string"||(isCellObject(cell)&&typeof cell.text==="string")){return null}
|
|
50
43
|
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}
|
|
51
|
-
function formatValue(value,kind,fmt){if(value===null||value===undefined){return ""}
|
|
44
|
+
function formatValue(value,kind,fmt,format){if(value===null||value===undefined){return ""}
|
|
52
45
|
if(typeof value==="boolean"){return value?fmt.labels.yes:fmt.labels.no}
|
|
53
|
-
if(typeof value==="number"&&kind==="number"){return
|
|
46
|
+
if(typeof value==="number"&&kind==="number"){return numberText(value,fmt.lang,format)}
|
|
54
47
|
if(typeof value==="string"&&kind==="datetime"){return formatDate(value,fmt)}
|
|
55
48
|
return String(value)}
|
|
56
49
|
function cellText(cell,column,fmt){if(isCellObject(cell)&&typeof cell.text==="string"){return cell.text}
|
|
57
|
-
return formatValue(cellValue(cell),column.kind,fmt)}
|
|
58
|
-
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}
|
|
50
|
+
return formatValue(cellValue(cell),column.kind,fmt,column.format)}
|
|
51
|
+
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}
|
|
59
52
|
if(value===null||value===""){return{rank:2,value:0}}
|
|
60
53
|
if(typeof value==="boolean"){return{rank:0,value:value?1:0}}
|
|
61
54
|
if(typeof value==="number"){return{rank:0,value}}
|
|
@@ -64,12 +57,46 @@ return{rank:1,value:String(value)}}
|
|
|
64
57
|
function compareKeys(a,b,direction){if(a.rank!==b.rank){return a.rank-b.rank}
|
|
65
58
|
if(a.rank===2){return 0}
|
|
66
59
|
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}
|
|
60
|
+
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 + "'.");
|
|
61
|
+
if(typeof source[name]!=="string"||!/\S/.test(source[name])) throw new Error("CanopyX: label '" + name + "' must be a string with visible text.");
|
|
62
|
+
if((name==="utcTime"||name==="localTime")&&source[name].indexOf("{time}")<0)throw new Error("CanopyX: label '"+name+"' must hold {time}.");labels[name]=source[name]})}
|
|
63
|
+
return Object.freeze(labels)}
|
|
64
|
+
function fill(template,values){return template.replace(/\{(\w+)\}/g, (match, name) => (values && Object.hasOwn(values, name) ? String(values[name]) : match));
|
|
65
|
+
}
|
|
66
|
+
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];
|
|
67
|
+
let rules=pluralRules.get(lang);if(!rules){try{rules=new Intl.PluralRules(lang||"en")}catch(_){rules=new Intl.PluralRules("en")}pluralRules.set(lang,rules)}
|
|
68
|
+
return forms[rules.select(Number(count))]??forms.other??template}
|
|
69
|
+
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)}
|
|
70
|
+
if(numberFormats.size>=16)numberFormats.delete(numberFormats.keys().next().value);numberFormats.set(key,format)}
|
|
71
|
+
return format}
|
|
72
|
+
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||"")}
|
|
73
|
+
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}
|
|
74
|
+
function withNonce(element){const nonce=pageNonce||document.querySelector("script[nonce]")?.nonce;if(nonce)element.nonce=nonce;return element}
|
|
75
|
+
function trusted(kind,value){if(trustPolicy===undefined){try{trustPolicy=global.trustedTypes?.createPolicy("canopyx",{createHTML:text=>text,createScript:text=>text,createScriptURL:text=>text})||null}
|
|
76
|
+
catch(_){trustPolicy=null}}
|
|
77
|
+
return trustPolicy?trustPolicy["create"+kind](value):value}
|
|
78
|
+
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.")}
|
|
79
|
+
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();
|
|
80
|
+
}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(); }
|
|
81
|
+
else if(c==="]"){closed=true;break}}
|
|
82
|
+
if(!closed)bad()}else if("()|^$*+?{}]".includes(unit))bad();let count=1;if(i<source.length&&"*+?".includes(source[i])){variable++;i++}
|
|
83
|
+
else if(source[i]==="{"){const match=/^\{(\d+)(?:,(\d*))?\}/.exec(source.slice(i));
|
|
84
|
+
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}
|
|
85
|
+
width +=count;if(width>256||variable>1)bad()}
|
|
86
|
+
if(variable&&!whole&&source[0]!=="^")bad();return new RegExp(whole?"^(?:"+source+")$":source,exact?"u":"iu")}
|
|
87
|
+
function wildcardMatch(pattern,value,exact){if(!exact){pattern=pattern.toLowerCase();value=value.toLowerCase()}
|
|
88
|
+
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++}
|
|
89
|
+
else if(pattern[p]==="*"){star=p++;retry=v}
|
|
90
|
+
else if(star>=0){p=star+1;v=++retry}
|
|
91
|
+
else return false}
|
|
92
|
+
while(pattern[p]==="*")p++;return p===pattern.length}
|
|
67
93
|
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}
|
|
68
94
|
function foldCode(c){if(c<0x80){return c>=65&&c<=90?c+32:c}
|
|
69
95
|
return EXTRA_LOWERCASE.get(c)||c}
|
|
70
96
|
function foldUnit(unit){return String.fromCharCode(foldCode(unit.charCodeAt(0)))}
|
|
71
97
|
function isSeparator(code){return code<128&&!((code>=48&&code<=57)||(code>=65&&code<=90)||(code>=97&&code<=122))}
|
|
72
|
-
function foldText(text){const source=String(text==null?"":text);
|
|
98
|
+
function foldText(text){const source=String(text==null?"":text);if(!/[^\0-\x7f]/.test(source)) return source.toLowerCase();
|
|
99
|
+
let out="";for(let i=0;i<source.length;i++){out +=foldUnit(source.charAt(i))}
|
|
73
100
|
return out}
|
|
74
101
|
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)}}
|
|
75
102
|
if(current){out.push(current)}
|
|
@@ -79,6 +106,37 @@ function trimSeparators(text){let start=0,end=text.length;while(start<end&&isSep
|
|
|
79
106
|
function rankLabel(label,foldedQuery){const l=trimSeparators(foldText(label));return l===foldedQuery?0:l.startsWith(foldedQuery)?1:2}
|
|
80
107
|
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)}
|
|
81
108
|
return out}
|
|
109
|
+
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)) {
|
|
110
|
+
const shape=svg.appendChild(document.createElementNS(ns,tag));for(const[,attribute,value]of attributes.matchAll(/([\w-]+)="([^"]*)"/g)) shape.setAttribute(attribute, value);
|
|
111
|
+
}
|
|
112
|
+
return svg}
|
|
113
|
+
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;
|
|
114
|
+
return text.includes(delimiter)||/["\r\n]/.test(text) ? '"' + text.replace(/"/g, '""') + '"' : text;
|
|
115
|
+
}
|
|
116
|
+
function gridScalar(cell,column){const value=cellValue(cell);if(value==null)return null;if(column.kind==="number"){if(!safeGridNumber(value))
|
|
117
|
+
throw new Error("CanopyX: numeric grid cells need finite values within the safe integer magnitude.");return value}
|
|
118
|
+
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;
|
|
119
|
+
if(!Number.isFinite(date))
|
|
120
|
+
throw new Error("CanopyX: date grid cells need ISO dates with an explicit time zone.");return date}
|
|
121
|
+
if(typeof value!=="string"&&typeof value!=="boolean")throw new Error("CanopyX: text grid cells need strings or booleans.");return value}
|
|
122
|
+
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}
|
|
123
|
+
return false}
|
|
124
|
+
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))
|
|
125
|
+
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)||
|
|
126
|
+
["text","searchText","title","tone","secondary"].some(name=>cell[name]!==undefined&&typeof cell[name]!=="string")||
|
|
127
|
+
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)||
|
|
128
|
+
cell.badges!==undefined&&(!Array.isArray(cell.badges)||cell.badges.some(badge=>!badge||typeof badge.text!=="string"||
|
|
129
|
+
["tone","title"].some(name=>badge[name]!==undefined&&typeof badge[name]!=="string"))))
|
|
130
|
+
throw new Error("CanopyX: malformed grid cell.");if(typeof cell.sort==="number")gridScalar(cell.sort,{kind:"number"})}
|
|
131
|
+
gridScalar(cell,declared.get(id))}}
|
|
132
|
+
return rows}
|
|
133
|
+
function gridDecoration(tone,className){if(tone!==undefined&&(typeof tone!=="string"||!/^[a-z][a-z0-9-]{0,31}$/.test(tone) || tone.endsWith("-bg")) ||
|
|
134
|
+
className!==undefined&&(typeof className!=="string"||!className.trim()||className.length>1024||/[\u0000-\u001f\u007f-\u009f]/.test(className)))
|
|
135
|
+
throw new Error("CanopyX: invalid highlight tone or className.")}
|
|
136
|
+
function gridSeriesValid(points){return Array.isArray(points)&&points.length<=4096&&points.every(point=>point&&typeof point==="object"&&!Array.isArray(point)&&
|
|
137
|
+
typeof point.period==="string"&&!!point.period.trim()&&point.period.length<=256&&
|
|
138
|
+
(point.value==null||Number.isFinite(point.value)&&Math.abs(point.value)<=Number.MAX_SAFE_INTEGER)&&
|
|
139
|
+
(point.state==null||typeof point.state==="string"&&point.state.length<=4096)&&(point.tone===undefined||typeof point.tone==="string"))}
|
|
82
140
|
const sidecarDocs=new Map();const sidecarWaiters=new Map();function sidecarKey(view,ref){return JSON.stringify([view,ref])}
|
|
83
141
|
function acceptSidecar(entry){if(!Array.isArray(entry)||entry.length!==3){return}
|
|
84
142
|
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])}}
|
|
@@ -86,7 +144,7 @@ const sidecarQueue=global.CanopyXSidecar=global.CanopyXSidecar||[];sidecarQueue.
|
|
|
86
144
|
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))}
|
|
87
145
|
const key=sidecarKey(view,ref);if(sidecarDocs.has(key)){const doc=sidecarDocs.get(key);sidecarDocs.delete(key);return Promise.resolve(doc)}
|
|
88
146
|
const pending=sidecarWaiters.get(key);if(pending){return pending.promise}
|
|
89
|
-
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}
|
|
147
|
+
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}
|
|
90
148
|
function badIndex(what){return new Error("CanopyX: search "+what+" does not match the manifest or is malformed.")}
|
|
91
149
|
function isCount(value){return Number.isInteger(value)&&value>=0}
|
|
92
150
|
function checkSearchIndex(index){if(!index||!isCount(index.entryCount)||!Array.isArray(index.entryFiles)||!Array.isArray(index.kinds)||
|
|
@@ -97,41 +155,11 @@ if(index.entryPacks===undefined&&index.shardPacks===undefined)return;const keys=
|
|
|
97
155
|
(index.entryFiles.length>0)!==(index.entryPacks.length>0)||(keys.length>0)!==(index.shardPacks.length>0)||
|
|
98
156
|
index.entryPacks.some((first,i)=>!isCount(first)||first>=index.entryFiles.length||(i?first<=index.entryPacks[i-1]:first))||
|
|
99
157
|
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")}}
|
|
100
|
-
function resolveShards(shards,keys,word){const bases=keys.filter((key)=>!/\.\d+$/.test(key));
|
|
101
|
-
const parts=(selected)=>selected.flatMap((key)=>[key].concat(keys.filter((part)=>part.startsWith(key+"."))
|
|
102
|
-
.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)}
|
|
103
|
-
return[]}
|
|
104
158
|
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)})}
|
|
105
159
|
cache.set(key,entry);return entry.pending};get.clear=()=>{cache.clear();bytes=0};return get}
|
|
106
|
-
const packBytes=pages=>pages.reduce((bytes,text)=>bytes +(typeof text==="string"?text.length*2:0),0);function
|
|
107
|
-
total +=list.length;if(last>=entryCount)throw badIndex("shard")});if(total!==count)throw badIndex("shard posting count");return doc}
|
|
108
|
-
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)}})}
|
|
109
|
-
if(ascending)return out;out.sort((a,b)=>a-b);return out.filter((value,i)=>!i||value!==out[i-1])}
|
|
110
|
-
function intersectPostings(a,b){let j=0;return a.filter((value)=>{while(j<b.length&&b[j]<value)j++;return b[j]===value})}
|
|
111
|
-
function labelToken(folded){let hash=2166136261;for(let i=0;i<folded.length;i++){hash=Math.imul(hash^folded.charCodeAt(i),16777619)}
|
|
112
|
-
return "\u0000" +("0000000" +(hash>>>0).toString(16)).slice(-8)}
|
|
113
|
-
function entryReference(value){return value===undefined||value===null||isIdentifier(value)}
|
|
114
|
-
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||
|
|
115
|
-
!isCount(item[1])||typeof item[2]!=="string"||!isCount(item[3])||typeof item[4]!=="string"||
|
|
116
|
-
(item[5]!=null&&!isCount(item[5]))||!entryReference(item[6])||!(item[7]===undefined||isIdentifier(item[7]))){throw badIndex("entry "+n)}
|
|
117
|
-
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})}
|
|
118
|
-
return records.get(n)}}
|
|
119
|
-
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]}
|
|
120
|
-
const parent=record.parent;if(parent)hit.context=parent.parent?parent.parent.label+" / "+parent.label:parent.label;validateSearchHit(hit);return hit}
|
|
121
|
-
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}
|
|
122
|
-
return low}
|
|
123
|
-
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])}
|
|
124
|
-
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]))}
|
|
125
|
-
return mergePostings(lists)}
|
|
126
|
-
async function run(query,queryWords,limit,sequence){const search=await index();if(!search){return{hits:[],total:0,truncated:false}}
|
|
127
|
-
const keyWords=queryWords.filter((word,i)=>queryWords.indexOf(word)===i&&
|
|
128
|
-
!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)}}
|
|
129
|
-
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)}}}
|
|
130
|
-
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)})}
|
|
131
|
-
return ranked}
|
|
132
|
-
let latest=0;return(query,limit)=>{const queryWords=words(query);if(!queryWords.length){return Promise.resolve({hits:[],total:0,truncated:false})}
|
|
160
|
+
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})}
|
|
133
161
|
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)}
|
|
134
|
-
return
|
|
162
|
+
return loadPart(SEARCH_PART).then(()=>(search||=SEARCH_IMPL.create(load,manifest,hints))(query,queryWords,limit))}}
|
|
135
163
|
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}}
|
|
136
164
|
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}}
|
|
137
165
|
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)}
|
|
@@ -162,14 +190,15 @@ if(kind==="inline"){if(!Array.isArray(view.roots))throw new Error("CanopyX: inli
|
|
|
162
190
|
throw new Error("CanopyX: unknown source kind '"+kind+"'.")}
|
|
163
191
|
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)}
|
|
164
192
|
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))}}
|
|
165
|
-
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.")}
|
|
166
|
-
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.
|
|
193
|
+
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.")}
|
|
194
|
+
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"))
|
|
195
|
+
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.")}
|
|
167
196
|
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||[]}
|
|
168
|
-
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()}
|
|
197
|
+
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()}
|
|
169
198
|
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()}}
|
|
170
|
-
buildIcons(icons){const templates=new Map();Object.keys(icons).forEach((key)=>{const holder=h("span","cx-icon");holder.setAttribute("aria-hidden","true");holder
|
|
199
|
+
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
|
|
171
200
|
templates.set(key,holder)});return templates}
|
|
172
|
-
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}
|
|
201
|
+
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}
|
|
173
202
|
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()}}
|
|
174
203
|
loadRoots(){if(!this.root.inlineItems){return this.loadPage(this.root,null)}
|
|
175
204
|
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()}
|
|
@@ -231,7 +260,7 @@ throw new Error("CanopyX: query filters must be unique declared IDs.");const fil
|
|
|
231
260
|
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}
|
|
232
261
|
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")}
|
|
233
262
|
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")}
|
|
234
|
-
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.
|
|
263
|
+
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++}
|
|
235
264
|
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)}
|
|
236
265
|
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}}
|
|
237
266
|
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}}
|
|
@@ -265,7 +294,7 @@ return false}};Object.assign(TreeGrid.prototype,ROW_SCROLLING);const ROW_VIEWPOR
|
|
|
265
294
|
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)}
|
|
266
295
|
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)?
|
|
267
296
|
(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}
|
|
268
|
-
if(rowEl._index!==index){rowEl.setAttribute("aria-rowindex",index+2);rowEl._index=index}
|
|
297
|
+
if(rowEl._index!==index){rowEl.setAttribute("aria-rowindex",index+2 +(this.headDepth||0));rowEl._index=index}
|
|
269
298
|
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}
|
|
270
299
|
const tab=w===tabRow?0:-1;if(rowEl._tab!==tab){rowEl.tabIndex=tab;rowEl._tab=tab}}
|
|
271
300
|
for(const rowEl of free){rowEl._w=null;this.observeDetail(rowEl,false);if(this.pool.length<200)this.pool.push(rowEl)}
|
|
@@ -276,13 +305,15 @@ Object.assign(TreeGrid.prototype,{fillRow(rowEl,w){rowEl._w=w;const kind=w.statu
|
|
|
276
305
|
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}
|
|
277
306
|
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(""))}
|
|
278
307
|
return}
|
|
279
|
-
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}
|
|
308
|
+
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}
|
|
280
309
|
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)}
|
|
281
310
|
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}
|
|
282
311
|
const expander=this.expandable(w)?"cx-exp":"cx-exp cx-leaf";if(slot.expCls!==expander){slot.exp.className=expander;slot.expCls=expander}
|
|
283
312
|
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()}
|
|
284
313
|
slot.iconEl=key?this.iconTemplates.get(key).cloneNode(true):null;if(slot.iconEl){slot.cell.insertBefore(slot.iconEl,slot.content)}
|
|
285
|
-
slot.iconKey=key}},
|
|
314
|
+
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)?
|
|
315
|
+
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}
|
|
316
|
+
const hosted=column.render?this.paintHostSlot(slot,node,column,index,w):null;if(hosted==="host"){return}
|
|
286
317
|
const decorated=hosted==="decorated";const text=cellText(value,column,this.fmt);if(slot.text.data!==text){slot.text.data=text}
|
|
287
318
|
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}
|
|
288
319
|
slot.cls=cls}
|
|
@@ -301,16 +332,17 @@ function hostBadge(badge){if(!badge||typeof badge!=="object"||typeof badge.text!
|
|
|
301
332
|
return badgeElement(badge)}
|
|
302
333
|
function attributeShape(element){let shape="";for(const attribute of element.attributes){shape +=attribute.name+"="+attribute.value+"\n"}
|
|
303
334
|
return shape}
|
|
304
|
-
function hostRenderers(renderers,columns){if(![Object.prototype,null].includes(Object.getPrototypeOf(renderers))||
|
|
335
|
+
function hostRenderers(renderers,columns,plugins){if(![Object.prototype,null].includes(Object.getPrototypeOf(renderers))||
|
|
305
336
|
Object.keys(renderers).some(id=>typeof renderers[id]!=="function"||!columns.some(column=>column.id===id)))
|
|
306
|
-
throw new Error("CanopyX: invalid column renderers.");return
|
|
337
|
+
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])
|
|
338
|
+
.map(column=>({[column.id]:named[column.renderer]})),renderers)}
|
|
307
339
|
function disposeHostSlot(slot){const lifetime=slot.hostLifetime;if(!lifetime)return;delete slot.hostLifetime;lifetime.dispose()}
|
|
308
340
|
function disposeRowHostCells(row){row._cells?.forEach(disposeHostSlot)}
|
|
309
341
|
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}
|
|
310
342
|
get signal(){this.#controller||=new AbortController();if(this.#cleanups===null)this.#controller.abort();return this.#controller.signal}
|
|
311
343
|
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}
|
|
312
344
|
dispose(){const cleanups=this.#cleanups;this.#cleanups=null;this.#controller?.abort();while(cleanups?.length){try{cleanups.pop()()}catch(error){global.console?.error(error)}}}}
|
|
313
|
-
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}:
|
|
345
|
+
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}:
|
|
314
346
|
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}
|
|
315
347
|
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"}
|
|
316
348
|
if(tree){slot.cell.className=slot.base;slot.cell.removeAttribute("title");toneColors(slot.cell,NO_TONE)}
|
|
@@ -324,7 +356,7 @@ Object.assign(TreeGrid.prototype,{renderDetail(node,element,plainHtml=false){con
|
|
|
324
356
|
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)}
|
|
325
357
|
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) + "%"; });
|
|
326
358
|
}
|
|
327
|
-
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
|
|
359
|
+
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
|
|
328
360
|
}},detailHeight(detail){if(detail.height>0){return detail.height*this.rowH}
|
|
329
361
|
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}
|
|
330
362
|
rowEl._auto=auto;if(!this.detailObserver){this.detailObserver=new ResizeObserver(()=>{this.measureNeeded=true;this.scheduleRender()})}
|
|
@@ -364,9 +396,10 @@ function cellControls(cell){const controls=Array.prototype.filter.call(cell.quer
|
|
|
364
396
|
function quietControls(cell){cell.querySelectorAll(CELL_CONTROLS).forEach((control)=>{if(control.tabIndex>=0)control.tabIndex=-1})}
|
|
365
397
|
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}
|
|
366
398
|
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}
|
|
367
|
-
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
|
|
399
|
+
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&&
|
|
400
|
+
(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)}
|
|
368
401
|
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)}
|
|
369
|
-
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}
|
|
402
|
+
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}
|
|
370
403
|
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":
|
|
371
404
|
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":
|
|
372
405
|
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":
|
|
@@ -408,8 +441,7 @@ if(w.status){this.setFocus(w,true);this.activate(w,true);return}
|
|
|
408
441
|
const interactive=hostControl(e.target);if(interactive&&!e.target.closest(".cx-exp")){this.setFocus(w,false);return}
|
|
409
442
|
if(w.h.detail){this.setFocus(w,true);this.select(w,{toggle:e.ctrlKey||e.metaKey,range:e.shiftKey});return}
|
|
410
443
|
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}
|
|
411
|
-
this.columns[index].hidden=!visible;const
|
|
412
|
-
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()}}}
|
|
444
|
+
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()}}}
|
|
413
445
|
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))}
|
|
414
446
|
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)}
|
|
415
447
|
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()}
|
|
@@ -453,9 +485,15 @@ await navigation.wait(this.source.page(parent===this.root?null:parent.h,cursor,t
|
|
|
453
485
|
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}
|
|
454
486
|
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)}}
|
|
455
487
|
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)}):
|
|
456
|
-
fill(this.labels.itemsLoaded,{count:count.format(items.length)}));this.stateChanged()}});
|
|
457
|
-
|
|
458
|
-
|
|
488
|
+
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);
|
|
489
|
+
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"||
|
|
490
|
+
typeof format.label!=="string"||!format.label.trim()||typeof format.extension!=="string"||!/^[a-z\d]{1,16}$/i.test(format.extension) ||
|
|
491
|
+
format.mime!==undefined&&typeof format.mime!=="string"||typeof format.write!=="function")
|
|
492
|
+
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})}
|
|
493
|
+
function registerExportFormat(format){format=exportFormat(format);EXPORT_FORMATS.set(format.id,format);return()=>EXPORT_FORMATS.get(format.id)===format&&EXPORT_FORMATS.delete(format.id)}
|
|
494
|
+
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))
|
|
495
|
+
.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}
|
|
496
|
+
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}
|
|
459
497
|
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}
|
|
460
498
|
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)}
|
|
461
499
|
if(hit.context){const context=h("span","cx-result-context");setText(context,hit.context);item.appendChild(context)}
|
|
@@ -466,19 +504,43 @@ fill(labels.searchFailed,{error:err&&err.message?err.message:err}))}}};const mov
|
|
|
466
504
|
if(active>=0){list.children[active].setAttribute("aria-selected","false")}
|
|
467
505
|
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=""}
|
|
468
506
|
close()}else{return}
|
|
469
|
-
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}
|
|
470
|
-
|
|
507
|
+
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)}}}
|
|
508
|
+
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);
|
|
509
|
+
function registerPart(name,part){if(!partName(name)||!part||typeof part.version!=="string"||typeof part.install!=="function"||
|
|
510
|
+
part.requires!==undefined&&!(Array.isArray(part.requires)&&part.requires.every(partName)))throw new Error("CanopyX: invalid part.");PART_CODE.set(name,part)}
|
|
511
|
+
function definePart(View,name,label,context){const parts=View.prototype.parts||={};return parts[name]={name,label,context,parts}}
|
|
512
|
+
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)}
|
|
513
|
+
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);
|
|
514
|
+
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")
|
|
515
|
+
.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")
|
|
516
|
+
.finally(()=>script.remove()).catch(error=>{part.scriptReady=null;throw error}));await Promise.all([style,code])}}
|
|
517
|
+
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})}
|
|
518
|
+
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)}
|
|
519
|
+
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")
|
|
471
520
|
throw new Error("CanopyX: invalid state column.");seen.add(c.id);return{id:c.id,width:c.width,hidden:c.hidden}}
|
|
472
521
|
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})})}
|
|
473
|
-
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})
|
|
522
|
+
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})
|
|
474
523
|
.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}}
|
|
475
524
|
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}}
|
|
476
|
-
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}}
|
|
477
|
-
this.
|
|
525
|
+
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}}
|
|
526
|
+
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)&&
|
|
478
527
|
(!attached.has(JSON.stringify(path))||!cached))continue;try{await this.expandPath(path,{select:false,focus:false,reveal:false,signal:current.signal})}
|
|
479
528
|
catch(error){/* Nodes removed since the snapshot are skipped. */ }
|
|
480
|
-
}},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
|
|
481
|
-
|
|
529
|
+
}},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"&&
|
|
530
|
+
(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))||
|
|
531
|
+
["requires","views","toolbarItems","menuItems","exportFormats","recordActions"].some(key=>item[key]!==undefined&&!Array.isArray(item[key]))||
|
|
532
|
+
["cellRenderers","columnTypes","editors","events","aggregates","detailRenderers","drawerSections"].some(key=>item[key]!==undefined&&Object(item[key])!==item[key])||
|
|
533
|
+
!(item.requires||[]).every(partName)||!(item.views||[]).every(kind=>kind==="tree"||kind==="grid")||
|
|
534
|
+
![...Object.values(item.cellRenderers||{}),...Object.values(item.events||{}),...Object.values(item.detailRenderers||{}),...Object.values(item.drawerSections||{})].every(fn)||
|
|
535
|
+
!(item.toolbarItems||[]).every(entry())||!(item.recordActions||[]).every(entry())||!(item.menuItems||[]).every(entry(true)))
|
|
536
|
+
throw new Error("CanopyX: invalid plugin '"+item.name+"'.");item.exportFormats?.forEach(exportFormat);return item}
|
|
537
|
+
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())}}}
|
|
538
|
+
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)}
|
|
539
|
+
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.")}
|
|
540
|
+
ordered.push(item)};byName.forEach(visit);return ordered}
|
|
541
|
+
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)}
|
|
542
|
+
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)}
|
|
543
|
+
function pluginItems(grid,key){return grid.plugins.flatMap(item=>item[key]||[])}
|
|
482
544
|
Object.assign(TreeGrid.prototype,{pathOf(w){const path=[];for(let node=w;node&&node!==this.root;node=node.parent){path.push(node.key)}
|
|
483
545
|
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])}}
|
|
484
546
|
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++}}
|
|
@@ -492,10 +554,7 @@ catch(error){if(!failure)failure=error}}
|
|
|
492
554
|
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}
|
|
493
555
|
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}
|
|
494
556
|
let state=null;try{state=JSON.parse(sessionStorage.getItem(key)||"null")}catch(e){state=null}
|
|
495
|
-
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(){
|
|
496
|
-
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}
|
|
497
|
-
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))}
|
|
498
|
-
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}
|
|
557
|
+
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}
|
|
499
558
|
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}}
|
|
500
559
|
doc.items.forEach((item)=>{const w=this.keyMap.get(item.id);if(!w){return}
|
|
501
560
|
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}
|
|
@@ -507,30 +566,48 @@ w.content=nodeContent(w.h)});this.inlineOrderVersion=(this.inlineOrderVersion||0
|
|
|
507
566
|
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})}}
|
|
508
567
|
const navigation=navigationScope(this,this.dataLifetime.signal);let doc;try{doc=await navigation.wait(this.source.counts(wanted,this.sourceRequest(this.dataLifetime.signal)))}
|
|
509
568
|
finally{navigation.dispose()}
|
|
510
|
-
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}});
|
|
511
|
-
},currentLink(){if(this.opts.parseLink){return(global.location.hash||"").replace(/^#/, "");
|
|
512
|
-
}
|
|
513
|
-
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.
|
|
569
|
+
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.
|
|
514
570
|
}
|
|
515
571
|
this.linkChanged=this.dataLifetime.signal;return this.ready.then(()=>undefined,()=>undefined)}
|
|
516
|
-
return this.openDeepLink()},
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
572
|
+
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));
|
|
573
|
+
}).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})}}
|
|
574
|
+
linkMethods(TreeGrid);function linkGridPart(View){(Object.hasOwn(View.prototype,"parts")?View.prototype.parts:View.prototype.parts={}).link=LINK_PART}
|
|
575
|
+
function linkReady(grid,ready){return Promise.all([ready,grid.linkStart()]).then(([result])=>result)}
|
|
576
|
+
function linkApi(grid){const api={};for(const name of LINK_METHODS.concat("getExternalFilters","getHighlight"))api[name]=(...args)=>grid[name](...args);return api}
|
|
577
|
+
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)}
|
|
578
|
+
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}))}
|
|
579
|
+
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");
|
|
580
|
+
if(!options||typeof options!=="object"||Array.isArray(options)||Object.keys(options).some(key=>!HIGHLIGHT_KEYS.includes(key))||
|
|
581
|
+
HIGHLIGHT_KEYS.slice(0,5).some(key=>options[key]!==undefined&&typeof options[key]!=="boolean")||
|
|
582
|
+
options.minLength!==undefined&&(!Number.isInteger(options.minLength)||options.minLength<1||options.minLength>32)||
|
|
583
|
+
options.filterTones!==undefined&&(!options.filterTones||typeof options.filterTones!=="object"||Array.isArray(options.filterTones)||
|
|
584
|
+
Object.entries(options.filterTones).some(([id,name])=>!columns.some(column=>column.id===id)||!tone(name))))
|
|
585
|
+
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})})}
|
|
586
|
+
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))||
|
|
587
|
+
value.modes!==undefined&&(!Array.isArray(value.modes)||!value.modes.length||new Set(value.modes).size!==value.modes.length||
|
|
588
|
+
value.modes.some(mode=>!all.includes(mode)))||
|
|
589
|
+
value.mode!==undefined&&!(value.modes||all).includes(value.mode)||value.density!==undefined&&!LAYOUT_DENSITIES.includes(value.density))
|
|
590
|
+
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"})}
|
|
591
|
+
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))
|
|
592
|
+
throw new Error("CanopyX: invalid saved layout.");if(options)result.layout={mode:options.modes.includes(layout.mode)?layout.mode:options.mode,density:layout.density}}
|
|
593
|
+
if(state.highlight!==undefined){if(typeof state.highlight!=="boolean")throw new Error("CanopyX: invalid saved highlight choice.");if(view.highlightOptions)result.highlight=state.highlight}
|
|
594
|
+
return result}
|
|
595
|
+
function densityRowHeight(view,density){const steps=DENSITY_STEP[view.kind];return Math.max(20,Math.min(64,view.baseRowH+steps[density]- steps[view.declaredDensity]))}
|
|
596
|
+
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}
|
|
597
|
+
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))
|
|
598
|
+
this.setLayout(state.layout).catch(error=>this.emit("error",{error}));if(state.highlight!==undefined&&state.highlight!==!!this.highlightOn)
|
|
599
|
+
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))
|
|
600
|
+
.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}
|
|
601
|
+
function gridDateInputMoment(text,local){const match=/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{1,3}))?)?$/.exec(text);
|
|
602
|
+
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}
|
|
603
|
+
function gridFreeze(value){if(value&&typeof value==="object"&&!Object.isFrozen(value)){Object.values(value).forEach(gridFreeze);Object.freeze(value)}
|
|
527
604
|
return value}
|
|
528
|
-
for(const name of["buildHeader","visibleColumns","applyWidths","setColumnWidth","startResize","cell","newCell","paintSlot","paintBadges","paintHostSlot","setSlotContent","setTimeZone","rowCells","heldCell","restoreCell","focusCell","revealInRow","revealInCell","settleCell"])
|
|
529
|
-
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){
|
|
605
|
+
for(const name of["buildHeader","visibleColumns","applyWidths","setColumnWidth","startResize","cell","newCell","paintSlot","paintBadges","cellTip","paintHostSlot","setSlotContent","setTimeZone","rowCells","heldCell","restoreCell","focusCell","revealInRow","revealInCell","settleCell"])
|
|
606
|
+
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)}
|
|
530
607
|
function gridDecorateButton(button,icon,ghost){button.classList.add("cx-btn");if(ghost)button.classList.add("ghost");if(icon)button.prepend(gridIcon(icon));return button}
|
|
531
608
|
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)}
|
|
532
609
|
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)}
|
|
533
|
-
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)||
|
|
610
|
+
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)||
|
|
534
611
|
!Array.isArray(values))
|
|
535
612
|
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)
|
|
536
613
|
throw new Error("CanopyX: invalid grid predicate arity.");if(["contains","startsWith"].includes(filter.operator)&&(column.kind!=="text"||values.some(value=>typeof value!=="string"))||
|
|
@@ -546,30 +623,19 @@ function gridSearchTerms(text,mode){if(mode!=="words")return text?[foldText(text
|
|
|
546
623
|
}
|
|
547
624
|
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)}))}
|
|
548
625
|
function gridQueryStart(query,patch){return{...query,...patch,offset:0,cursor:undefined,revision:undefined}}
|
|
549
|
-
function
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
if(!Number.isFinite(date))
|
|
553
|
-
throw new Error("CanopyX: date grid cells need ISO dates with an explicit time zone.");return date}
|
|
554
|
-
if(typeof value!=="string"&&typeof value!=="boolean")throw new Error("CanopyX: text grid cells need strings or booleans.");return value}
|
|
555
|
-
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))
|
|
556
|
-
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)||
|
|
557
|
-
["text","searchText","title","tone","secondary"].some(name=>cell[name]!==undefined&&typeof cell[name]!=="string")||
|
|
558
|
-
cell.secondary!==undefined&&cell.secondary.length>4096||cell.series!==undefined&&!gridSeriesValid(cell.series)||
|
|
559
|
-
cell.badges!==undefined&&(!Array.isArray(cell.badges)||cell.badges.some(badge=>!badge||typeof badge.text!=="string"||
|
|
560
|
-
["tone","title"].some(name=>badge[name]!==undefined&&typeof badge[name]!=="string"))))
|
|
561
|
-
throw new Error("CanopyX: malformed grid cell.")}
|
|
562
|
-
gridScalar(cell,declared.get(id))}}
|
|
563
|
-
return rows}
|
|
626
|
+
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||
|
|
627
|
+
Object.keys(format).some(key=>!["minimumFractionDigits","maximumFractionDigits","prefix","suffix"].includes(key))||
|
|
628
|
+
[format.prefix,format.suffix].some(text=>text!==undefined&&(typeof text!=="string"||!text||text.length>16)))throw new Error("CanopyX: invalid number format.")}
|
|
564
629
|
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)
|
|
565
630
|
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)}
|
|
566
|
-
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)||
|
|
631
|
+
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)||
|
|
567
632
|
column.width!==undefined&&(!Number.isInteger(column.width)||column.width<40)||
|
|
568
|
-
["wrap","autoWidth","fixed","rowHeader","alwaysVisible"].some(name=>column[name]!==undefined&&typeof column[name]!=="boolean")||
|
|
633
|
+
["wrap","autoWidth","fixed","rowHeader","alwaysVisible","editable","filterable","exportable"].some(name=>column[name]!==undefined&&typeof column[name]!=="boolean")||
|
|
569
634
|
column.flex!==undefined&&!(typeof column.flex==="number"&&column.flex>=0&&column.flex<=100)||column.autoSize!==undefined&&!["content","header","none"].includes(column.autoSize)||
|
|
570
635
|
column.pinned!==undefined&&![null,"left","right"].includes(column.pinned)||column.priority!==undefined&&!gridPriority(column.priority)||
|
|
571
|
-
column.fixed&&column.width===undefined)
|
|
572
|
-
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.
|
|
636
|
+
column.fixed&&column.width===undefined||column.tooltip!==undefined&&column.tooltip!=="auto"&&!view.columns.some(item=>item.id===column.tooltip))
|
|
637
|
+
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)}
|
|
638
|
+
if(column.aggregatable!==undefined&&(typeof column.aggregatable!=="boolean"||column.aggregatable&&column.kind!=="number")||
|
|
573
639
|
column.section!==undefined&&(typeof column.section!=="string"||!column.section.trim()||column.section.length>256))
|
|
574
640
|
throw new Error("CanopyX: invalid column aggregate or drawer section.")}
|
|
575
641
|
if(view.columns.every(column=>column.hidden)||view.columns.filter(column=>column.rowHeader).length>1)
|
|
@@ -580,7 +646,7 @@ view.noun!==undefined&&(typeof view.noun!=="string"||!view.noun.trim()||view.nou
|
|
|
580
646
|
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))
|
|
581
647
|
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||
|
|
582
648
|
new Set(view.searchColumns).size!==view.searchColumns.length||view.searchColumns.some(id=>!ids.has(id))))
|
|
583
|
-
throw new Error("CanopyX: search columns must identify unique declared columns.");if(view.autoRowHeight!==undefined&&typeof view.autoRowHeight!=="boolean"||
|
|
649
|
+
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"||
|
|
584
650
|
view.rowHeight!==undefined&&view.rowHeight!=="auto"&&(!Number.isInteger(view.rowHeight)||view.rowHeight<20||view.rowHeight>64)||
|
|
585
651
|
view.estimatedRowHeight!==undefined&&(!Number.isInteger(view.estimatedRowHeight)||view.estimatedRowHeight<20||view.estimatedRowHeight>64)||
|
|
586
652
|
!Number.isInteger(view.pageSize)||view.pageSize<1||view.pageSize>10000||
|
|
@@ -588,7 +654,7 @@ view.estimatedRowHeight!==undefined&&(!Number.isInteger(view.estimatedRowHeight)
|
|
|
588
654
|
throw new Error("CanopyX: invalid grid source navigation.");if(view.source.navigation==="cursor"&&(view.source.kind!=="host"||view.presentation!=="paged"))
|
|
589
655
|
throw new Error("CanopyX: cursors need a hosted paged grid.");if(view.presentation!==undefined&&!["continuous","paged"].includes(view.presentation)||
|
|
590
656
|
view.pageLength!==undefined&&(!Number.isInteger(view.pageLength)||view.pageLength<1||view.pageLength>10000))
|
|
591
|
-
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)}}
|
|
657
|
+
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)}}
|
|
592
658
|
function gridQuery(view,input,strict){input=input||{};const cursor=view.source.navigation==="cursor",type=cursor?"gridCursorQuery":"gridQuery";if(typeof input!=="object"||Array.isArray(input)||
|
|
593
659
|
input.protocol!==undefined&&input.protocol!==PROTOCOL||input.type!==undefined&&input.type!==type||
|
|
594
660
|
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)
|
|
@@ -597,7 +663,7 @@ throw new Error("CanopyX: invalid grid query window or search.");if(input.revisi
|
|
|
597
663
|
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))
|
|
598
664
|
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)||
|
|
599
665
|
!["asc","desc"].includes(sort.direction))
|
|
600
|
-
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)}
|
|
666
|
+
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)}
|
|
601
667
|
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)||
|
|
602
668
|
page.matched>page.total||page.offset>page.matched||!Array.isArray(page.items)||
|
|
603
669
|
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)}
|
|
@@ -605,14 +671,17 @@ function checkedGridPage(page,view,query,accepted){if(query.revision!==undefined
|
|
|
605
671
|
(page.revision!==accepted.revision||accepted.total!==undefined&&page.total!==accepted.total||
|
|
606
672
|
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))}}
|
|
607
673
|
function gridOrder(left,right){return left===right?0:left<right?-1:1}
|
|
608
|
-
function
|
|
609
|
-
function
|
|
610
|
-
return
|
|
674
|
+
function gridSortScalar(cell,column){return isCellObject(cell)&&cell.sort!=null?cell.sort:gridScalar(cell,column)}
|
|
675
|
+
function gridCompare(left,right,column,direction){return gridKeyCompare(gridSortScalar(left.cells[column.id],column),gridSortScalar(right.cells[column.id],column),column,direction)}
|
|
676
|
+
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}
|
|
677
|
+
function gridMatches(row,predicate,column){return cellMatches(row.cells[column.id],predicate,column)}
|
|
611
678
|
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)&&
|
|
612
|
-
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}
|
|
613
|
-
return gridOrder(a.id,b.id)}):rows}
|
|
679
|
+
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}
|
|
680
|
+
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}
|
|
681
|
+
return gridOrder(rows[a].id,rows[b].id)}).map(index=>rows[index])}
|
|
614
682
|
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}
|
|
615
|
-
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)}}}
|
|
683
|
+
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)}
|
|
684
|
+
return ordered.findIndex(row=>row.id===id)},row(id){return view.rows.find(row=>row.id===id)||null}}}
|
|
616
685
|
function gridCursor(cursor){requireIdentifier(cursor,"grid cursor");if(cursor.length>4096)throw new Error("CanopyX: cursor exceeds 4096 code units.")}
|
|
617
686
|
function validateGridCursorPage(page,view,query,accepted){checkDocument(page,"gridCursorPage");requireIdentifier(page.revision,"grid revision");if(page.view!==view.id||!validStructuralCount(page.offset)||
|
|
618
687
|
page.total!==null&&!validStructuralCount(page.total)||page.matched!==null&&!validStructuralCount(page.matched)||
|
|
@@ -621,26 +690,42 @@ page.total!==null&&page.matched!==null&&page.matched>page.total||
|
|
|
621
690
|
!page.items.length&&(page.offset!==0||next)||!page.offset&&previous||
|
|
622
691
|
page.matched!==null&&next!==(end<page.matched)||page.total!==null&&next&&end>=page.total||
|
|
623
692
|
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)}
|
|
624
|
-
|
|
693
|
+
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):
|
|
694
|
+
typeof scalar==="boolean"?"b:"+scalar:"n:"+scalar}
|
|
695
|
+
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||
|
|
696
|
+
input.search!==request.search||input.includeCurrentFilter!==request.includeCurrentFilter||
|
|
697
|
+
!Number.isInteger(input.matched)||input.matched<0||input.matched>MAX_PROTOCOL_INTEGER||
|
|
698
|
+
!Number.isInteger(input.totalValues)||input.totalValues<0||input.totalValues>input.matched||
|
|
699
|
+
!Array.isArray(input.items)||input.items.length>request.limit||input.items.length>input.totalValues||
|
|
700
|
+
!input.items.length&&input.totalValues>0||input.truncated!==(input.items.length<input.totalValues))
|
|
701
|
+
throw new Error("CanopyX: facet response must match its accepted revision, projection and bounded counts.");const widened=!request.includeCurrentFilter&&
|
|
702
|
+
gridResultShape(gridColumnProjection(request.query,request.column))!==gridResultShape(request.query);if(Number.isInteger(snapshot.total)&&input.matched>snapshot.total||Number.isInteger(snapshot.matched)&&
|
|
703
|
+
(widened?input.matched<snapshot.matched:input.matched!==snapshot.matched))
|
|
704
|
+
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)
|
|
705
|
+
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)
|
|
706
|
+
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})}
|
|
707
|
+
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=[]}
|
|
708
|
+
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"))
|
|
625
709
|
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?
|
|
626
710
|
this.view.source.kind==="inline"?0:300:this.view.searchDelayMs:options.searchDelayMs;if(!Number.isInteger(this.searchDelayMs)||this.searchDelayMs<0||this.searchDelayMs>5000)
|
|
627
|
-
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||
|
|
711
|
+
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||
|
|
628
712
|
!Number.isInteger(this.requestTimeoutMs)||this.requestTimeoutMs<1||this.requestTimeoutMs>2147483647)
|
|
629
|
-
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"))
|
|
713
|
+
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"))
|
|
630
714
|
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()}
|
|
631
|
-
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)}}
|
|
715
|
+
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)}}
|
|
716
|
+
return go}
|
|
632
717
|
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)}
|
|
633
718
|
getQuery(){return this.acceptedQuery||null}
|
|
634
719
|
queryForChange(includeQueuedInput){const query=this.pendingQuery||this.acceptedQuery||{};return includeQueuedInput&&this.searchTimer!=null&&!this.searchComposing?
|
|
635
720
|
gridQueryStart(query,{search:this.searchInput.value}):query}
|
|
636
721
|
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;
|
|
637
722
|
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;
|
|
638
|
-
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);
|
|
723
|
+
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);
|
|
639
724
|
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)}}
|
|
640
725
|
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}
|
|
641
|
-
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}
|
|
726
|
+
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}
|
|
642
727
|
this.render();if(hadFocus){const element=this.focused&&this.rendered.get(this.focused);(element||this.grid).focus({preventScroll:true})}
|
|
643
|
-
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})}
|
|
728
|
+
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})}
|
|
644
729
|
throw error}}
|
|
645
730
|
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(()=>{})}
|
|
646
731
|
sort(sorts){return this.setQuery(gridQueryStart(this.queryForChange(true),{sort:sorts}))}
|
|
@@ -658,7 +743,7 @@ observeDetail(){}
|
|
|
658
743
|
trimInactiveCache(){this.trimPages()}
|
|
659
744
|
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()}
|
|
660
745
|
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()}
|
|
661
|
-
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")}}
|
|
746
|
+
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")}}
|
|
662
747
|
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)
|
|
663
748
|
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.")}
|
|
664
749
|
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}
|
|
@@ -671,20 +756,20 @@ if(!this.rowCache.has(index))offsets.add(Math.floor(index / this.pageSize) * thi
|
|
|
671
756
|
if(this.focused?.status&&this.focused._ri>=0)offsets.add(Math.floor(this.focused._ri / this.pageSize) * this.pageSize);
|
|
672
757
|
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);
|
|
673
758
|
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))}
|
|
674
|
-
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])}
|
|
759
|
+
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])}
|
|
675
760
|
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:
|
|
676
761
|
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,
|
|
677
762
|
pageCount:this.paged?Math.ceil(count / this.pageLength) : count ? 1 : 0 } });
|
|
678
763
|
},goToPage(index){if(this.cursor||!this.paged||!this.acceptedQuery||!Number.isInteger(index)||index<0)
|
|
679
|
-
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)}
|
|
680
|
-
if(!this.paged||!this.acceptedQuery||!Number.isInteger(length)||length<1||length>Math.max(10000,this.view.source.kind==="inline"?this.view.rows.length:0))
|
|
764
|
+
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))}
|
|
765
|
+
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))
|
|
681
766
|
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,
|
|
682
767
|
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||
|
|
683
768
|
paging.direction==="previous"&&end!==paging.edge)
|
|
684
769
|
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)
|
|
685
770
|
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}
|
|
686
771
|
if(direction!=="first"&&cursor===undefined){if(!this.pendingQuery)return Promise.resolve(this.acceptedQuery);direction="first"}
|
|
687
|
-
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)}
|
|
772
|
+
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)}
|
|
688
773
|
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\})/);
|
|
689
774
|
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}
|
|
690
775
|
this.pageButtons.forEach((button,index)=>{button.disabled=busy||(this.cursor?index===0?this.pageOffset===0:
|
|
@@ -694,17 +779,21 @@ throw new Error("CanopyX: cursor keys stay inside the accepted page.");if(this.p
|
|
|
694
779
|
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 }));
|
|
695
780
|
const operation=this.navigatePageQuery(request);navigation.epoch=this.queryEpoch;await operation}
|
|
696
781
|
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)
|
|
697
|
-
throw new Error("CanopyX: row position is outside the accepted result page.");const epoch=this.queryEpoch,ownsFocus=focus&&this.canvas.contains(document.activeElement);
|
|
698
|
-
|
|
782
|
+
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&&
|
|
783
|
+
!this.destroyed&&!this.dataLifetime.signal.aborted&&epoch===this.queryEpoch&&!this.pendingQuery&&
|
|
784
|
+
this.pendingNavigation===navigation&&this.rowCache.get(index)===initial&&this.focused===initial&&
|
|
785
|
+
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))
|
|
786
|
+
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})}
|
|
787
|
+
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)
|
|
699
788
|
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}
|
|
700
789
|
style.setProperty("--cx-min-h",(this.toolbar.hidden?0:[...host.children].reduce((height,child)=>
|
|
701
790
|
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)}
|
|
702
791
|
this.scheduleRender()}));this.resizeObserver.observe(grid);this.resizeObserver.observe(this.head)},setLogicalTop(top){if(!this.hasGridSize()){this.hiddenScrollTop=top;return}
|
|
703
792
|
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)}
|
|
704
793
|
button.setAttribute("aria-label",cell.getAttribute("aria-label")+ ", "+sorts[index].direction+", " +(index+1))}}
|
|
705
|
-
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
|
|
794
|
+
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})}
|
|
706
795
|
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}
|
|
707
|
-
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 || ""
|
|
796
|
+
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 || ""
|
|
708
797
|
}):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}
|
|
709
798
|
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}
|
|
710
799
|
else if(slot.missing){slot.cell.classList.remove("p-missing");slot.missing=false}
|
|
@@ -721,178 +810,190 @@ if(changed){this.total=this.rowOffsets.top(this.rowOffsets.count);this.syncScrol
|
|
|
721
810
|
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));
|
|
722
811
|
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()}
|
|
723
812
|
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}
|
|
724
|
-
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};
|
|
813
|
+
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}
|
|
725
814
|
function gridRank(column){const value=column.priority;return typeof value==="number"?value:value==="optional"?10000:value==="essential"||value===undefined&&column.identity?0:5000}
|
|
726
815
|
function gridLevel(column){const rank=gridRank(column);return rank<2?0:rank<10000?1:2}
|
|
727
816
|
function gridOptions(column){return column.presenter||{}}
|
|
817
|
+
const COLUMN_DIGITS=new Map(),COLUMN_NUMERIC_CHARS="0123456789 .,:;+-−/()[]\u00a0\u202f",COLUMN_HAS_NUMERIC=/[0-9\s.,:;+\-−/()\[\]]/;
|
|
818
|
+
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)}
|
|
819
|
+
const variant=getComputedStyle(sample).fontVariantNumeric;if(owned)sample.remove();return variant}
|
|
820
|
+
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())}
|
|
821
|
+
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)}
|
|
822
|
+
document.body.appendChild(probe);const zero=glyphs[0].getBoundingClientRect().width / 50, numeric = new Map(), plain = new Map();
|
|
823
|
+
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;
|
|
824
|
+
if(Math.abs(delta)>0.02)(mode?plain:numeric).set(character.charCodeAt(0),delta)}}
|
|
825
|
+
probe.remove();variants.set(variant,{numeric:numeric.size?numeric:null,plain:plain.size?plain:null})}
|
|
826
|
+
const advances=variants.get(variant),digits=/[0-9]/.test(text) ? advances.numeric : advances.plain;
|
|
827
|
+
if(!digits)return measured;let width=measured;for(let index=0;index<text.length;index++){width +=digits.get(text.charCodeAt(index))||0}
|
|
828
|
+
return width}
|
|
728
829
|
function gridTextual(column){return column.kind==="text"&&!["statusStrip","sparklineBars","sparklineLine","boolean","progress"].includes(gridOptions(column).kind)}
|
|
729
|
-
function
|
|
730
|
-
|
|
731
|
-
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}
|
|
732
|
-
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})}
|
|
733
|
-
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}
|
|
734
|
-
queueMicrotask(()=>this.destroyed||this.applyWidths())}
|
|
735
|
-
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;
|
|
736
|
-
for(const column of cols)total +=column.renderWidth -=(column.renderWidth-this.fitSize(column,level))*share}
|
|
737
|
-
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?
|
|
830
|
+
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}
|
|
831
|
+
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?
|
|
738
832
|
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:
|
|
739
|
-
Math.min(need,Math.max(column.headNeed||0,gridTextual(column)?this.fitCh||88:0))))},
|
|
833
|
+
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))
|
|
834
|
+
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)
|
|
835
|
+
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; }
|
|
836
|
+
excess -=cut(low);for(const column of group)column.renderWidth=Math.min(column.renderWidth,Math.max(low,floor.get(column)))}
|
|
837
|
+
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);
|
|
740
838
|
flex=flex.filter(column=>column.maxW-column.renderWidth>0.5)}
|
|
741
839
|
const identity=columns.find(column=>column.identity),widest=columns.filter(column=>!column.identity&&
|
|
742
840
|
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));
|
|
743
|
-
grow(identity,Infinity);grow(widest,Infinity);return target},
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
if(total>width
|
|
747
|
-
|
|
841
|
+
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))
|
|
842
|
+
if(total>width&&remaining>1){column.fitHidden=true;total -=this.fitSize(column,1);remaining--}
|
|
843
|
+
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))
|
|
844
|
+
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"}
|
|
845
|
+
grid.columns=["left","none","right"].flatMap(side=>grid.columns.filter(column=>column.pin===side))}
|
|
846
|
+
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}
|
|
847
|
+
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})}
|
|
848
|
+
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}
|
|
849
|
+
queueMicrotask(()=>this.destroyed||this.applyWidths())}
|
|
850
|
+
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}}
|
|
851
|
+
for(const column of this.columns){column.reserve=0;if(!column.fixed&&!column.sized)column.width=this.fitSize(column)}
|
|
852
|
+
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))}
|
|
853
|
+
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)+
|
|
748
854
|
(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:
|
|
749
855
|
Math.min(kind==="longText"||kind==="tags"||kind==="list"||column.wrap?320:420,text(value,kind==="enum"&&!quiet?small:undefined))+
|
|
750
|
-
(kind==="progress"?80:kind==="enum"
|
|
856
|
+
(kind==="progress"?80:kind==="enum"?18:kind==="state"&&!quiet?15:kind==="tags"||kind==="list"?48:0));sampled++}
|
|
751
857
|
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}
|
|
752
858
|
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})}
|
|
753
|
-
return result};function
|
|
754
|
-
className!==undefined&&(typeof className!=="string"||!className.trim()||className.length>1024||/[\u0000-\u001f\u007f-\u009f]/.test(className)))
|
|
755
|
-
throw new Error("CanopyX: invalid highlight tone or className.")}
|
|
756
|
-
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||
|
|
859
|
+
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||
|
|
757
860
|
rule.target!==undefined&&!["row","cell"].includes(rule.target)||rule.tone===undefined&&rule.className===undefined)
|
|
758
861
|
throw new Error("CanopyX: invalid highlight rule.");const{column,operator,values}=rule;gridPredicate({column,operator,values},columns,budget);gridDecoration(rule.tone,rule.className)}}
|
|
759
862
|
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)}
|
|
760
863
|
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;
|
|
761
|
-
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"||
|
|
864
|
+
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"||
|
|
762
865
|
["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()}
|
|
763
866
|
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||
|
|
764
867
|
options.limit!==undefined&&(!Number.isInteger(options.limit)||options.limit<1||options.limit>16)))invalid();if((options.minimum===undefined)!==(options.scaleMaximum===undefined)||options.minimum!==undefined&&
|
|
765
|
-
(![options.minimum,options.scaleMaximum].every(value=>Number.isFinite(value)&&Math.abs(value)<=Number.MAX_SAFE_INTEGER)||options.minimum>=options.scaleMaximum))invalid();if(options.
|
|
766
|
-
|
|
868
|
+
(![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)||
|
|
869
|
+
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();
|
|
870
|
+
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||
|
|
767
871
|
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)=>
|
|
768
872
|
!point||!Number.isFinite(point.upperBound)||Math.abs(point.upperBound)>Number.MAX_SAFE_INTEGER||
|
|
769
873
|
typeof point.tone!=="string"||index>0&&options.thresholds[index-1].upperBound>=point.upperBound)))invalid()}
|
|
770
874
|
function gridSeries(cell){return isCellObject(cell)&&cell.series||[]}
|
|
771
|
-
function
|
|
772
|
-
typeof point.period==="string"&&!!point.period.trim()&&point.period.length<=256&&
|
|
773
|
-
(point.value==null||Number.isFinite(point.value)&&Math.abs(point.value)<=Number.MAX_SAFE_INTEGER)&&
|
|
774
|
-
(point.state==null||typeof point.state==="string"&&point.state.length<=4096)&&(point.tone===undefined||typeof point.tone==="string"))}
|
|
775
|
-
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"}
|
|
875
|
+
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"}
|
|
776
876
|
function gridPresenterLink(raw){if(typeof raw!=="string"||raw.length>8192||/[\u0000-\u001f\u007f]/.test(raw)||!raw.trim()) return null;
|
|
777
877
|
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;
|
|
778
878
|
}catch(_){/* Invalid destinations retain plain text. */ }
|
|
779
879
|
return null}
|
|
780
880
|
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}}
|
|
781
|
-
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(
|
|
782
|
-
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));
|
|
881
|
+
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)}
|
|
882
|
+
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));
|
|
783
883
|
}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)}
|
|
784
884
|
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));
|
|
785
885
|
const preview=add(text.length>budget?text.slice(0,budget).replace(/[\uD800-\uDBFF]$/,"")+" …":text,"cx-wrapped-text"+(options.mono?" p-mono":""));
|
|
786
886
|
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);}
|
|
787
887
|
}else add(text);if(object)for(const badge of cell.badges||[])element.appendChild(badgeElement(badge));return true}}
|
|
788
|
-
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")}
|
|
789
|
-
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}
|
|
790
|
-
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=[];
|
|
791
|
-
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})}
|
|
792
|
-
const segment=Math.max(2,Math.min(6,(width-buckets.length+1)/buckets.length));
|
|
793
|
-
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)}
|
|
794
|
-
element.appendChild(strip);return}
|
|
795
|
-
const values=series.filter(point=>typeof point.value==="number");if(!values.length){setText(element,"—");element.classList.add("p-missing");return}
|
|
796
|
-
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)));
|
|
797
|
-
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))));
|
|
798
|
-
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];
|
|
799
|
-
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)}}
|
|
800
888
|
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})}
|
|
801
889
|
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}
|
|
802
890
|
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}
|
|
803
891
|
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}
|
|
804
892
|
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}
|
|
805
|
-
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)}
|
|
893
|
+
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)}
|
|
806
894
|
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()}
|
|
807
|
-
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}
|
|
808
|
-
const
|
|
895
|
+
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}
|
|
896
|
+
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?
|
|
897
|
+
link.dispatchEvent(new MouseEvent("click",{bubbles:true,cancelable:true,ctrlKey:event.ctrlKey,metaKey:event.metaKey,shiftKey:event.shiftKey})):
|
|
898
|
+
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&&
|
|
809
899
|
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)
|
|
810
|
-
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();
|
|
900
|
+
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}
|
|
811
901
|
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}
|
|
812
|
-
if(event.key==="Enter"){event.preventDefault();
|
|
902
|
+
if(event.key==="Enter"){event.preventDefault();this.activateRow(row,true);return}}
|
|
813
903
|
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}
|
|
814
904
|
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":
|
|
815
905
|
if(at>0)nextControl=at-1;else if(column>0){nextColumn--;nextControl=-1}
|
|
816
906
|
else{this.setFocus(row,true);event.preventDefault();return}
|
|
817
907
|
break;case "ArrowRight":
|
|
818
|
-
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":
|
|
819
|
-
if(column>=0){this.setFocus(row,true);event.preventDefault();return}
|
|
908
|
+
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":
|
|
909
|
+
if(this.gridPickKey(event,row,column))return;if(column>=0){this.setFocus(row,true);event.preventDefault();return}
|
|
820
910
|
case "Enter":
|
|
821
911
|
if(row.status){if(row.status==="error")this.retry().catch(()=>{})}
|
|
822
912
|
else if(!control)this.focusCell(row,Math.max(0,column),Math.max(0,at),true);else return;event.preventDefault();return;case " ":
|
|
823
913
|
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])}
|
|
824
914
|
event.preventDefault();return;default:return}
|
|
825
|
-
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
|
|
915
|
+
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)})}
|
|
916
|
+
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}
|
|
917
|
+
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}}
|
|
826
918
|
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}
|
|
827
919
|
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. */ }
|
|
828
920
|
}}
|
|
829
|
-
try{this.initialState=stored&&stored.current?this.readGridState(stored.current):null}catch(_){this.initialState=null}},initializeState(){
|
|
921
|
+
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)||
|
|
830
922
|
!Number.isFinite(state.scrollTop)||state.scrollTop<0||state.scrollTop>MAX_PROTOCOL_INTEGER*this.rowH||!Array.isArray(state.selection))
|
|
831
|
-
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")||
|
|
923
|
+
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")||
|
|
832
924
|
!Number.isInteger(paging.offset)||paging.offset<0||paging.offset>MAX_PROTOCOL_INTEGER||this.cursor&&paging.offset!==0)
|
|
833
925
|
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)}
|
|
834
|
-
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||
|
|
926
|
+
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||
|
|
835
927
|
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}))}))}
|
|
836
928
|
catch(_){/* A disabled or full storage leaves the current session usable. */ }
|
|
837
|
-
}});function
|
|
929
|
+
}});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()))
|
|
930
|
+
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";
|
|
931
|
+
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;
|
|
932
|
+
const parsed=typeof value==="string"&&/^\d{4}-\d\d-\d\dT\d\d:\d\d/.test(value) ? new Date(value) : null;
|
|
933
|
+
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.")}
|
|
934
|
+
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")}
|
|
935
|
+
for(const field of CAL_FIELDS)if(field!=="allDay"&&event[field]!=null){cells[field]=String(event[field]);used.add(field)}
|
|
936
|
+
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})}
|
|
937
|
+
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)}
|
|
938
|
+
return roots}
|
|
939
|
+
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")
|
|
940
|
+
throw new Error("CanopyX: invalid saved facet panel state.");if(this.view.facets||this.opts.facets)state.facets={open:facets.open}}
|
|
941
|
+
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}
|
|
942
|
+
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())
|
|
943
|
+
.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")||
|
|
944
|
+
!!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)=>
|
|
945
|
+
(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}
|
|
946
|
+
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)})}}}
|
|
947
|
+
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}
|
|
948
|
+
let keyboardInput=false;if(typeof document!=="undefined"){document.addEventListener("keydown",()=>{keyboardInput=true},true);document.addEventListener("pointerdown",()=>{keyboardInput=false},true)}
|
|
949
|
+
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}
|
|
950
|
+
function gridOption(value,text){const option=h("option");option.value=value;setText(option,text);return option}
|
|
951
|
+
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))}
|
|
952
|
+
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\})/){
|
|
838
953
|
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}))}
|
|
839
954
|
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?
|
|
840
|
-
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(
|
|
955
|
+
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})})}
|
|
841
956
|
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)}
|
|
842
|
-
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})}
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
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())}}
|
|
847
|
-
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))}
|
|
848
|
-
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)}
|
|
957
|
+
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})}
|
|
958
|
+
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(()=>{})))}
|
|
959
|
+
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())}}
|
|
960
|
+
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)}
|
|
849
961
|
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(()=>
|
|
850
962
|
this.runGridHook(signal=>this.opts.onChart({query:this.getQuery(),selection:this.getSelectionDescriptor?.()||
|
|
851
|
-
{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
|
|
852
|
-
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}
|
|
963
|
+
{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")||
|
|
964
|
+
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}
|
|
853
965
|
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)}}
|
|
854
|
-
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.
|
|
855
|
-
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))}
|
|
966
|
+
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))}
|
|
856
967
|
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)}}
|
|
857
|
-
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"))}
|
|
858
|
-
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
|
|
968
|
+
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"))}
|
|
969
|
+
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)}}
|
|
859
970
|
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)}
|
|
860
971
|
for(const column of[...grid.cols].reverse())if(column.effectivePin==="right"){column.pinOffset=end;end+=width(column)}
|
|
861
972
|
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)})}
|
|
862
|
-
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
|
|
863
|
-
|
|
864
|
-
setText(script,payload.textContent);if(payload.nonce)script.nonce=payload.nonce;document.head.appendChild(script);script.remove()}
|
|
865
|
-
else{const runtime=FILTER_RUNTIME_URL||global.CanopyXFilterEditorBase||document.baseURI,hashed=FILTER_RUNTIME_URL&&/\.[a-f\d]{16}\.js(?:[?#]|$)/.test(FILTER_RUNTIME_URL);
|
|
866
|
-
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()}}
|
|
867
|
-
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}
|
|
868
|
-
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())}}
|
|
973
|
+
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)}
|
|
974
|
+
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())}}
|
|
869
975
|
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)+'"]'))}
|
|
870
|
-
catch(error){this.setNotice(this.labels.gridLoadFailed,true);this.emit("error",{error})}};
|
|
871
|
-
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
Object.assign(DataGrid.prototype,{prepareExport(options){if(!this.view.reporting&&["fileName","locale","timeZone","onProgress"].some(key=>options?.[key]!==undefined))
|
|
875
|
-
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.
|
|
876
|
-
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:
|
|
877
|
-
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))})}
|
|
878
|
-
(page.matched!==null&&page.matched!==page.total||page.nextCursor===undefined&&page.offset+page.items.length!==page.total)))
|
|
879
|
-
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)}]))})}}
|
|
880
|
-
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)}}
|
|
881
|
-
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:
|
|
882
|
-
row.cells[column.id]?.text,delimiter)).join(delimiter))}
|
|
883
|
-
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)||
|
|
976
|
+
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")}
|
|
977
|
+
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)}
|
|
978
|
+
catch(error){resolutionError=error}
|
|
979
|
+
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)})}
|
|
980
|
+
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))
|
|
981
|
+
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||
|
|
982
|
+
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:
|
|
983
|
+
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)||
|
|
884
984
|
index<(this.cursor?this.pageOffset:0)||index>=this.rowCount())
|
|
885
|
-
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."))},
|
|
886
|
-
|
|
887
|
-
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)})}
|
|
985
|
+
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)
|
|
986
|
+
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)})}
|
|
888
987
|
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)
|
|
889
|
-
.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"
|
|
890
|
-
|
|
891
|
-
|
|
988
|
+
.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)}
|
|
989
|
+
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())}}
|
|
990
|
+
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}
|
|
892
991
|
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}
|
|
893
|
-
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
|
|
894
|
-
|
|
895
|
-
|
|
992
|
+
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&&
|
|
993
|
+
{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"])
|
|
994
|
+
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)}}}
|
|
995
|
+
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}
|
|
996
|
+
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})}
|
|
896
997
|
function checkBranch(){return{value:null,mask:0,children:new Map()}}
|
|
897
998
|
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)}
|
|
898
999
|
if(branch.value!==null)throw new Error("CanopyX: a checking path cannot have duplicate intents.");branch.value=item.checked}
|
|
@@ -935,8 +1036,8 @@ w&&!w.status&&!w.h.detail&&
|
|
|
935
1036
|
super.onKey(event)}
|
|
936
1037
|
getState(){const state=super.getState();if(this.checkMode){state.checkboxes=this.checkMode;state.checkIntents=this.checks.snapshot()}
|
|
937
1038
|
return state}
|
|
938
|
-
|
|
939
|
-
({path:this.pathOf(w),label:w.h.label,detail:!!w.h.detail}));const
|
|
1039
|
+
outlineElement(){const model=this.checks,items=this.rows.filter(w=>!w.status).map(w=>
|
|
1040
|
+
({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}
|
|
940
1041
|
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}}
|
|
941
1042
|
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}
|
|
942
1043
|
finally{this.paintCheckedRows()}}
|
|
@@ -972,10 +1073,10 @@ cancelInteraction(){const operation=this.pendingInteraction;if(!operation)return
|
|
|
972
1073
|
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()}
|
|
973
1074
|
withInteractionReplacement(work){this.interactionReplacementDepth=(this.interactionReplacementDepth||0)+ 1;try{return work()}
|
|
974
1075
|
finally{this.interactionReplacementDepth--;if(!this.interactionReplacementDepth){const controllers=this.interactionAbortQueue;this.interactionAbortQueue=null;if(controllers)for(const controller of controllers)controller.abort()}}}
|
|
975
|
-
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})}
|
|
1076
|
+
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})}
|
|
976
1077
|
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))}})}
|
|
977
1078
|
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}
|
|
978
|
-
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"}
|
|
1079
|
+
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}
|
|
979
1080
|
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,"")}
|
|
980
1081
|
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}
|
|
981
1082
|
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})}}
|
|
@@ -1023,13 +1124,18 @@ this.dropRow=null}
|
|
|
1023
1124
|
dropPlacement(event,row){const bounds=row.getBoundingClientRect(),fraction=(event.clientY-bounds.top)/ bounds.height;
|
|
1024
1125
|
return fraction<0.25?"before":fraction>0.75?"after":"inside"}
|
|
1025
1126
|
destroy(){this.clearDropRow();this.dragLease=null;super.destroy()}};function createTreeGrid(host,options){if(!host||!options||!options.view){throw new Error("CanopyX: createTreeGrid needs a host element and a view document.")}
|
|
1026
|
-
const existing=activeGrids.get(host);if(existing)return existing;const grid=new TreeGrid(host,options);const api=Object.
|
|
1127
|
+
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"])
|
|
1128
|
+
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);
|
|
1129
|
+
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}
|
|
1027
1130
|
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.")}
|
|
1028
1131
|
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.")}
|
|
1029
1132
|
const registration=Object.assign({},configured.get(viewId),options,{view:undefined});configured.set(viewId,registration);return()=>{if(configured.get(viewId)===registration)configured.delete(viewId)}}
|
|
1030
1133
|
function unconfigure(viewId){requireIdentifier(viewId,"view ID");return configured.delete(viewId)}
|
|
1134
|
+
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" });
|
|
1135
|
+
global.addEventListener("beforeprint",()=>document.querySelectorAll('[data-cx-mount="visible"]:not([data-canopyx-mounted])').forEach(host=>mount(host)))}
|
|
1136
|
+
watcher.observe(host)}
|
|
1031
1137
|
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}
|
|
1032
1138
|
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}}
|
|
1033
|
-
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)};
|
|
1034
|
-
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}
|
|
1035
|
-
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
|
|
1139
|
+
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}
|
|
1140
|
+
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}
|
|
1141
|
+
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:{}));if(typeof document!=="undefined"){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)}}})(runtimeHost);const CanopyX=runtimeHost.CanopyX;const{protocol,createTreeGrid,createDataGrid,createCalendar,enhance,grid,configure,unconfigure,mount,sources,badge,labels,registerExportFormat,use,registerPart,setNonce}=CanopyX;export{protocol,createTreeGrid,createDataGrid,createCalendar,enhance,grid,configure,unconfigure,mount,sources,badge,labels,registerExportFormat,use,registerPart,setNonce};export default CanopyX;
|