@evotecit/canopyx 0.3.15 → 0.3.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CanopyX.Html/Assets/canopyx-board.css +50 -0
- package/CanopyX.Html/Assets/canopyx-board.js +39 -0
- package/CanopyX.Html/Assets/canopyx-buckets.js +15 -0
- package/CanopyX.Html/Assets/canopyx-calculations.css +7 -0
- package/CanopyX.Html/Assets/canopyx-calculations.js +52 -0
- package/CanopyX.Html/Assets/canopyx-calendar-edit.js +3 -3
- package/CanopyX.Html/Assets/canopyx-calendar.css +5 -24
- package/CanopyX.Html/Assets/canopyx-calendar.js +14 -19
- package/CanopyX.Html/Assets/canopyx-cell-range.js +16 -14
- package/CanopyX.Html/Assets/canopyx-changes.css +6 -24
- package/CanopyX.Html/Assets/canopyx-changes.js +18 -15
- package/CanopyX.Html/Assets/canopyx-compare.css +37 -0
- package/CanopyX.Html/Assets/canopyx-compare.js +12 -0
- package/CanopyX.Html/Assets/canopyx-data.js +3 -3
- package/CanopyX.Html/Assets/canopyx-editing.css +2 -2
- package/CanopyX.Html/Assets/canopyx-editing.js +2 -2
- package/CanopyX.Html/Assets/canopyx-enhance.js +4 -4
- package/CanopyX.Html/Assets/canopyx-export.js +54 -13
- package/CanopyX.Html/Assets/canopyx-facets.css +14 -5
- package/CanopyX.Html/Assets/canopyx-facets.js +19 -15
- package/CanopyX.Html/Assets/canopyx-filter-editor.css +71 -85
- package/CanopyX.Html/Assets/canopyx-filter-editor.js +20 -16
- package/CanopyX.Html/Assets/canopyx-flow-checks.css +2 -0
- package/CanopyX.Html/Assets/canopyx-flow-checks.js +5 -4
- package/CanopyX.Html/Assets/canopyx-flow-dialog.css +2 -0
- package/CanopyX.Html/Assets/canopyx-flow-dialog.js +1 -1
- package/CanopyX.Html/Assets/canopyx-flow-entry.css +136 -12
- package/CanopyX.Html/Assets/canopyx-flow-entry.js +5 -5
- package/CanopyX.Html/Assets/canopyx-flow-facts.js +2 -0
- package/CanopyX.Html/Assets/canopyx-flow-module.mjs +5 -5
- package/CanopyX.Html/Assets/canopyx-flow.js +8 -7
- package/CanopyX.Html/Assets/canopyx-format-editor.js +2 -2
- package/CanopyX.Html/Assets/canopyx-formatting.js +10 -9
- package/CanopyX.Html/Assets/canopyx-grid-module.mjs +185 -131
- package/CanopyX.Html/Assets/canopyx-grid.css +228 -96
- package/CanopyX.Html/Assets/canopyx-grid.js +185 -131
- package/CanopyX.Html/Assets/canopyx-grouping.css +22 -15
- package/CanopyX.Html/Assets/canopyx-grouping.js +24 -17
- package/CanopyX.Html/Assets/canopyx-interactive.css +197 -74
- package/CanopyX.Html/Assets/canopyx-interactive.js +38 -45
- package/CanopyX.Html/Assets/canopyx-labels-de.js +177 -9
- package/CanopyX.Html/Assets/canopyx-labels-es.js +177 -9
- package/CanopyX.Html/Assets/canopyx-labels-fr.js +176 -8
- package/CanopyX.Html/Assets/canopyx-labels-it.js +176 -8
- package/CanopyX.Html/Assets/canopyx-labels-pl.js +176 -8
- package/CanopyX.Html/Assets/canopyx-layouts.css +48 -20
- package/CanopyX.Html/Assets/canopyx-layouts.js +36 -14
- package/CanopyX.Html/Assets/canopyx-link.js +2 -2
- package/CanopyX.Html/Assets/canopyx-live.css +1 -3
- package/CanopyX.Html/Assets/canopyx-live.js +20 -15
- package/CanopyX.Html/Assets/canopyx-matrix.css +1 -8
- package/CanopyX.Html/Assets/canopyx-matrix.js +11 -11
- package/CanopyX.Html/Assets/canopyx-module.mjs +38 -45
- package/CanopyX.Html/Assets/canopyx-office-export.js +2936 -0
- package/CanopyX.Html/Assets/canopyx-path.css +45 -0
- package/CanopyX.Html/Assets/canopyx-path.js +28 -0
- package/CanopyX.Html/Assets/canopyx-pivot.css +75 -0
- package/CanopyX.Html/Assets/canopyx-pivot.js +72 -0
- package/CanopyX.Html/Assets/canopyx-plan.css +56 -0
- package/CanopyX.Html/Assets/canopyx-plan.js +67 -0
- package/CanopyX.Html/Assets/canopyx-presenters.js +2 -2
- package/CanopyX.Html/Assets/canopyx-record-panel.css +11 -2
- package/CanopyX.Html/Assets/canopyx-record-panel.js +18 -12
- package/CanopyX.Html/Assets/canopyx-reporting-module.mjs +225 -164
- package/CanopyX.Html/Assets/canopyx-reporting.css +240 -107
- package/CanopyX.Html/Assets/canopyx-reporting.js +225 -164
- package/CanopyX.Html/Assets/canopyx-stack.css +35 -0
- package/CanopyX.Html/Assets/canopyx-stack.js +24 -0
- package/CanopyX.Html/Assets/canopyx-time-slider.css +18 -0
- package/CanopyX.Html/Assets/canopyx-time-slider.js +27 -0
- package/CanopyX.Html/Assets/canopyx-timeline.js +6 -5
- package/CanopyX.Html/Assets/canopyx-tree-tools.css +36 -35
- package/CanopyX.Html/Assets/canopyx-tree-tools.js +15 -12
- package/CanopyX.Html/Assets/canopyx-ui-menu.css +41 -0
- package/CanopyX.Html/Assets/canopyx-ui-menu.js +64 -0
- package/CanopyX.Html/Assets/canopyx-ui-navigation.css +73 -0
- package/CanopyX.Html/Assets/canopyx-ui-navigation.js +39 -0
- package/CanopyX.Html/Assets/canopyx-ui-overlays.css +79 -0
- package/CanopyX.Html/Assets/canopyx-ui-overlays.js +42 -0
- package/CanopyX.Html/Assets/canopyx-ui-picker.css +12 -0
- package/CanopyX.Html/Assets/canopyx-ui-picker.js +6 -0
- package/CanopyX.Html/Assets/canopyx-ui-stepper.css +174 -0
- package/CanopyX.Html/Assets/canopyx-ui-stepper.js +48 -0
- package/CanopyX.Html/Assets/canopyx-ui-tabs.css +153 -0
- package/CanopyX.Html/Assets/canopyx-ui-tabs.js +47 -0
- package/CanopyX.Html/Assets/canopyx-values.css +5 -0
- package/CanopyX.Html/Assets/canopyx-values.js +13 -0
- package/CanopyX.Html/Assets/canopyx-view-intents.css +12 -0
- package/CanopyX.Html/Assets/canopyx-view-intents.js +56 -0
- package/CanopyX.Html/Assets/canopyx.css +197 -71
- package/CanopyX.Html/Assets/canopyx.js +29 -19
- package/JavaScript/board.d.ts +30 -0
- package/JavaScript/changes.d.ts +4 -1
- package/JavaScript/facets.d.ts +5 -3
- package/JavaScript/flow.d.ts +6 -0
- package/JavaScript/grid.d.ts +153 -17
- package/JavaScript/hosts.d.ts +14 -1
- package/JavaScript/index.d.ts +4 -2
- package/JavaScript/live.d.ts +2 -0
- package/JavaScript/locales/de.mjs +177 -9
- package/JavaScript/locales/es.mjs +177 -9
- package/JavaScript/locales/fr.mjs +176 -8
- package/JavaScript/locales/it.mjs +176 -8
- package/JavaScript/locales/pl.mjs +176 -8
- package/JavaScript/matrix.d.ts +4 -1
- package/JavaScript/path.d.ts +33 -0
- package/JavaScript/pivot.d.ts +135 -0
- package/JavaScript/plan.d.ts +23 -0
- package/JavaScript/protocol.d.ts +16 -3
- package/JavaScript/records.d.ts +20 -4
- package/JavaScript/stack.d.ts +28 -0
- package/JavaScript/time-slider.d.ts +58 -0
- package/JavaScript/ui.d.ts +261 -0
- package/JavaScript/view-intents.d.ts +45 -0
- package/README.md +22 -3
- package/ThirdParty/OfficeIMO/LICENSE +21 -0
- package/ThirdParty/OfficeIMO/README.md +13 -0
- package/package.json +76 -3
|
@@ -75,7 +75,8 @@ function fill(template,values){return template.replace(/\{(\w+)\}/g, (match, nam
|
|
|
75
75
|
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];
|
|
76
76
|
let rules=pluralRules.get(lang);if(!rules){try{rules=new Intl.PluralRules(lang||"en")}catch(_){rules=new Intl.PluralRules("en")}pluralRules.set(lang,rules)}
|
|
77
77
|
return forms[rules.select(Number(count))]??forms.other??template}
|
|
78
|
-
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",hiddenColumns:"[one]+{count} column[other]+{count} 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)=>{
|
|
78
|
+
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",hiddenColumns:"[one]+{count} column[other]+{count} 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)=>{const uiLabel=/^(?:tabs(?:More(?:Selected)?|ScrollPrevious|ScrollNext|Section|Position|Pause|Play|MotionDisabled)|stepper(?:StepOf|Back|Next|Finish|Skip|SaveDraft|AllSteps|Optional|Completed|Skipped|Issues?)|dialogClose|drawer(?:Close|Previous|Next)|state(?:Loading|Retry|Details))$/.test(name);
|
|
79
|
+
if(!Object.hasOwn(DEFAULT_LABELS,name)&&!/^(?:gridEdit|format|calendar|flow|matrix|change|board|plan|path|stack|time|compare|viewIntent)[A-Z]\w{0,63}$/.test(name) && !uiLabel) throw new Error("CanopyX: there is no label '" + name + "'.");
|
|
79
80
|
if(typeof source[name]!=="string"||!/\S/.test(source[name])) throw new Error("CanopyX: label '" + name + "' must be a string with visible text.");
|
|
80
81
|
if((name==="utcTime"||name==="localTime")&&source[name].indexOf("{time}")<0)throw new Error("CanopyX: label '"+name+"' must hold {time}.");labels[name]=source[name]})}
|
|
81
82
|
return Object.freeze(labels)}
|
|
@@ -95,7 +96,7 @@ function overlayTheme(host,element,inherit=false){for(const name of["data-hfx-st
|
|
|
95
96
|
const theme=getComputedStyle(host),parent=inherit&&host.parentElement&&getComputedStyle(host.parentElement);element.style.colorScheme=theme.colorScheme;const copied=[];for(const name of theme)if((name.startsWith("--cx-")||name.startsWith("--hfx-"))&&(!parent||theme.getPropertyValue(name)!==parent.getPropertyValue(name))){element.style.setProperty(name,theme.getPropertyValue(name));copied.push(name)}
|
|
96
97
|
return copied}
|
|
97
98
|
function watchOverlayTheme(host,element,signal){if(signal?.aborted)return()=>{};let copied=[];const refresh=()=>{for(const name of copied)element.style.removeProperty(name);copied=overlayTheme(host,element,element.parentElement===host.parentElement)};const observer=new MutationObserver(refresh);for(let owner=host;owner;owner=owner.parentElement)observer.observe(owner,{attributes:true,attributeFilter:THEME_ATTRIBUTES});const media=["(prefers-color-scheme:dark)","(prefers-contrast:more)","(forced-colors:active)"].map(query=>matchMedia(query));for(const query of media)query.addEventListener("change",refresh);const dispose=()=>{observer.disconnect();for(const query of media)query.removeEventListener("change",refresh);signal?.removeEventListener("abort",dispose)};signal?.addEventListener("abort",dispose,{once:true});dispose.refresh=refresh;refresh();return dispose}
|
|
98
|
-
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}
|
|
99
|
+
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:"Changed fields: {count}"}));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();
|
|
99
100
|
}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(); }
|
|
100
101
|
else if(c==="]"){closed=true;break}}
|
|
101
102
|
if(!closed)bad()}else if("()|^$*+?{}]".includes(unit))bad();let count=1;if(i<source.length&&"*+?".includes(source[i])){variable++;i++}
|
|
@@ -141,15 +142,18 @@ if(typeof value!=="string"&&typeof value!=="boolean")throw new Error("CanopyX: t
|
|
|
141
142
|
function gridExactTokens(column){return column.kind==="text"&&(column.valueOrder?.length||Object.keys(column.valueLabels||{}).length)}
|
|
142
143
|
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"&&!gridExactTokens(column)&&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}
|
|
143
144
|
return false}
|
|
144
|
-
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]));
|
|
145
|
-
throw new Error("CanopyX: invalid flat row fields.");
|
|
145
|
+
function validateGridRows(rows,columns,ids=new Set(),repair=null){if(!Array.isArray(rows))throw new Error("CanopyX: grid rows must be an array.");const declared=new Map(columns.map(column=>[column.id,column])),repaired=new Map();let result=rows;rows.forEach((row,index)=>{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))
|
|
146
|
+
throw new Error("CanopyX: invalid flat row fields.");let copy=null;const own=()=>copy||(result===rows&&(result=rows.slice()),result[index]=copy={...row,cells:{...row.cells}});try{gridDecoration(row.tone,row.className)}catch(error){if(!repair)throw error;delete own().tone;delete copy.className}
|
|
147
|
+
for(const id of Object.keys(row.cells)){const column=declared.get(id);try{if(!column)throw new Error("CanopyX: undeclared grid column "+id);validateGridCell(row.cells[id],column)}
|
|
148
|
+
catch(error){if(!repair)throw error;const cells=own().cells;if(column)cells[id]=repair(cells[id],column);else delete cells[id];const seen=repaired.get(id);if(seen)seen.count++;else repaired.set(id,{row:row.id,count:1})}}});for(const[id,seen]of repaired)
|
|
149
|
+
console.warn("CanopyX: "+seen.count+" cell(s) in column "+id+" did not fit the column and were coerced, emptied or dropped (first row "+seen.row+").");return result}
|
|
150
|
+
function validateGridCell(cell,column){if(isCellObject(cell)){if(Array.isArray(cell)||
|
|
146
151
|
["text","searchText","title","tone","secondary"].some(name=>cell[name]!==undefined&&typeof cell[name]!=="string")||
|
|
147
152
|
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)||
|
|
148
153
|
cell.badges!==undefined&&(!Array.isArray(cell.badges)||cell.badges.some(badge=>!badge||typeof badge.text!=="string"||
|
|
149
154
|
["tone","title"].some(name=>badge[name]!==undefined&&typeof badge[name]!=="string"))))
|
|
150
155
|
throw new Error("CanopyX: malformed grid cell.");if(typeof cell.sort==="number")gridScalar(cell.sort,{kind:"number"})}
|
|
151
|
-
gridScalar(cell,
|
|
152
|
-
return rows}
|
|
156
|
+
gridScalar(cell,column)}
|
|
153
157
|
function gridDecoration(tone,className){if(tone!==undefined&&(typeof tone!=="string"||!/^[a-z][a-z0-9-]{0,31}$/.test(tone) || tone.endsWith("-bg")) ||
|
|
154
158
|
className!==undefined&&(typeof className!=="string"||!className.trim()||className.length>1024||/[\u0000-\u001f\u007f-\u009f]/.test(className)))
|
|
155
159
|
throw new Error("CanopyX: invalid highlight tone or className.")}
|
|
@@ -278,7 +282,7 @@ value.sort===undefined||typeof value!=="object")throw new Error("CanopyX: query
|
|
|
278
282
|
const named=new Set(value.filters);if(named.size!==value.filters.length||value.filters.some(id=>!(grid.view.filters||[]).some(f=>f.id===id)))
|
|
279
283
|
throw new Error("CanopyX: query filters must be unique declared IDs.");const filters=Object.freeze((grid.view.filters||[]).filter(f=>named.has(f.id)).map(f=>f.id));return Object.freeze({version:1,sort,filters})}
|
|
280
284
|
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}
|
|
281
|
-
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");watchTheme(host,this.lifetime.signal);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")}
|
|
285
|
+
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");watchTheme(host,this.lifetime.signal);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("data-cx-finish-allow","empty-band");grid.setAttribute("aria-label",this.view.label);if(this.multi){grid.setAttribute("aria-multiselectable","true")}
|
|
282
286
|
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++}
|
|
283
287
|
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);if(column.headerDescription)cell.setAttribute("aria-description",column.headerDescription);cell.title=column.headerDescription??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";button.title=column.headerDescription??column.title;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");title.title=column.headerDescription??column.title;setText(title,column.title);cell.appendChild(title)}
|
|
284
288
|
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}}
|
|
@@ -508,11 +512,10 @@ fill(this.labels.itemsLoaded,{count:count.format(items.length)}));this.stateChan
|
|
|
508
512
|
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"||
|
|
509
513
|
typeof format.label!=="string"||!format.label.trim()||typeof format.extension!=="string"||!/^[a-z\d]{1,16}$/i.test(format.extension) ||
|
|
510
514
|
format.mime!==undefined&&typeof format.mime!=="string"||
|
|
515
|
+
format.values!==undefined&&!["raw","display"].includes(format.values)||
|
|
511
516
|
format.showInMenu!==undefined&&typeof format.showInMenu!=="boolean"||typeof format.write!=="function")
|
|
512
|
-
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",showInMenu:format.showInMenu!==false,write:format.write})}
|
|
517
|
+
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",showInMenu:format.showInMenu!==false,values:format.values,write:format.write})}
|
|
513
518
|
function registerExportFormat(format){format=exportFormat(format);EXPORT_FORMATS.set(format.id,format);return()=>EXPORT_FORMATS.get(format.id)===format&&EXPORT_FORMATS.delete(format.id)}
|
|
514
|
-
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))
|
|
515
|
-
.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}
|
|
516
519
|
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}
|
|
517
520
|
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}
|
|
518
521
|
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)}
|
|
@@ -524,8 +527,10 @@ fill(labels.searchFailed,{error:err&&err.message?err.message:err}))}}};const mov
|
|
|
524
527
|
if(active>=0){list.children[active].setAttribute("aria-selected","false")}
|
|
525
528
|
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=""}
|
|
526
529
|
close()}else{return}
|
|
527
|
-
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
|
|
528
|
-
|
|
530
|
+
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 lifetime=new AbortController();let flyout=null,pending=null;const ready=()=>flyout?Promise.resolve(flyout):pending||=loadPart(SHARED_PARTS["ui-overlays"]).then(()=>{if(!lifetime.signal.aborted)flyout=UI_TOOLS.toolbarFlyout(anchorElement,panel,isOpen,lifetime.signal);return flyout}).catch(error=>{pending=null;throw error});const invoke=method=>{if(lifetime.signal.aborted||!isOpen())return;if(flyout){flyout[method]();return}
|
|
531
|
+
ready().then(value=>{if(!lifetime.signal.aborted&&isOpen())value?.[method]()}).catch(error=>{if(!lifetime.signal.aborted)(anchorElement.closest(".cx-host")||anchorElement)
|
|
532
|
+
.dispatchEvent(new CustomEvent("canopyx:error",{detail:{error},bubbles:true}))})};return{position:()=>invoke("position"),open:()=>invoke("open"),close:()=>flyout?.close(),dispose:()=>lifetime.abort()}}
|
|
533
|
+
const PART_URL=typeof document!=="undefined"&&document.currentScript?.src;const PARTS={"ui-overlays":["68a7687b9c3c8a28","canopyx-ui-overlays.83acc4346a232940.js","canopyx-ui-overlays.bc49b6f13527aecd.css"],"ui-menu":["c86ee2a16b5a5a46","canopyx-ui-menu.1414af91bc295de8.js","canopyx-ui-menu.29b983c01571b5c3.css"],"ui-picker":["02ff3779dd1c1ef4","canopyx-ui-picker.7fc22075e98b3e14.js","canopyx-ui-picker.a10010ec2d8143ae.css"],"ui-navigation":["e7815ee277df3a57","canopyx-ui-navigation.86b48ef9eaffe773.js","canopyx-ui-navigation.4dd22a646eb71b44.css"],"ui-tabs":["96f562187c7e0732","canopyx-ui-tabs.4ea5e653f6919c8e.js","canopyx-ui-tabs.68bc5521fcc174fe.css"],"ui-stepper":["89aeb7d73c4b8160","canopyx-ui-stepper.0198464c5b259da1.js","canopyx-ui-stepper.fe35ecb9b43b2b2e.css"],"tree-tools":["ab6490d0ae0e0aff","canopyx-tree-tools.23262806ed53e5de.js","canopyx-tree-tools.9c5a978314bf0a86.css"],"presenters":["d5f9d61bb730974b","canopyx-presenters.d71f9e12d83969d9.js"],"filter-editor":["3baf5349dd74fe9f","canopyx-filter-editor.a25a9997f0aef871.js","canopyx-filter-editor.6faf0c36e139d75f.css"],"export":["fa85a4b0e8aead4a","canopyx-export.f18d1ef109ccf0c6.js"],"office-export":["4fb7a4d570390ee9","canopyx-office-export.7e62c385b884e75b.js"],"history":["7d9901d2fe04df72","canopyx-history.9432e724bf14c9ed.js"],"pager":["3dcab112113081dd","canopyx-pager.15bb475e4365fe79.js","canopyx-pager.e39f321db5bdfde4.css"],"enhance":["c0f4773cf3b59919","canopyx-enhance.7b4dab125769b947.js"],"data":["bab770170bd2cfdd","canopyx-data.34325d1fd693886e.js"],"forms":["0d77c25f4fa14425","canopyx-forms.3b18e30a2aeb56dd.js","canopyx-forms.e3a0e1f016287057.css"],"flow":["93f8d2eb57cc1770","canopyx-flow.c3f65a21ced2d654.js","canopyx-flow.68af38159226dd08.css"],"flow-host":["6f335df747cc5f05","canopyx-flow-host.ea0be90307b19d0f.js","canopyx-flow-host.87b00b9ed5899f1f.css"],"flow-checks":["1710216916cb32a5","canopyx-flow-checks.5a3e04709a556310.js","canopyx-flow-checks.e97a4514fd32bde5.css"],"flow-options":["60450342239ded26","canopyx-flow-options.d31458a54aa4b1e7.js"],"flow-pick":["e5f79a9e81e93898","canopyx-flow-pick.e17b209908086225.js","canopyx-flow-pick.7ec005490120da23.css"],"flow-records":["d02b7dc1c7f75805","canopyx-flow-records.5efc3b14cf70b524.js","canopyx-flow-records.cf62ae7260a46ec4.css"],"flow-drafts":["40dc8e50169fa271","canopyx-flow-drafts.67fb3ab7fbc22606.js","canopyx-flow-drafts.f8535eb8992ea36a.css"],"flow-preview":["3d8ffe456323db95","canopyx-flow-preview.4731603add2f8e84.js","canopyx-flow-preview.f1c740bc97130a7d.css"],"flow-command":["a6c75cf736c93444","canopyx-flow-command.f3608c38cc18f333.js","canopyx-flow-command.279d6ad310e39e97.css"],"flow-tasks":["8d9ea504e81e8fcd","canopyx-flow-tasks.eeb43b791491b43b.js","canopyx-flow-tasks.5c0dc5743e02c670.css"],"flow-dialog":["c3b57a7478b94a93","canopyx-flow-dialog.a98f91c1fe0ade3b.js","canopyx-flow-dialog.98c7ea17adc9a223.css"],"flow-links":["2c14b19c9236e90d","canopyx-flow-links.7b6cfe42d28b6067.js"],"flow-endpoint":["f5cddfc9545e0920","canopyx-flow-endpoint.b31146202bd76aac.js"],"flow-facts":["385b5e61ed487e16","canopyx-flow-facts.8dad3bc07a71fdb0.js"],"flow-consent":["22121778bc0d07c5","canopyx-flow-consent.749b97f13039bc71.js","canopyx-flow-consent.9f6580d3673ae8bf.css"],"matrix":["e5fa5cb0660b953a","canopyx-matrix.84cbd5879b0e07fa.js","canopyx-matrix.b01ffc8cdafd463d.css"],"changes":["419bb03b4580fd5c","canopyx-changes.86727fd2ca1714dd.js","canopyx-changes.5403d88f9169c173.css"],"editing":["25fd83789bef4fcb","canopyx-editing.53aebff05d75884c.js","canopyx-editing.4b3c01342a64d4e3.css"],"column-groups":["cd6d93e033719439","canopyx-column-groups.922e21b9bdf57efa.js","canopyx-column-groups.6a6c9704c4ee7884.css"],"cell-range":["53c37a658f4f7afb","canopyx-cell-range.e8215f22827b9003.js","canopyx-cell-range.ce87ff1c2023feae.css"],"links":["f1ab7e32b501ce37","canopyx-links.d96addeefb3eccf5.js"],"search":["521eebd6e1bbbd35","canopyx-search.14c69f4551c69f23.js"],"formatting":["7d7402e30ec311ca","canopyx-formatting.6bfafbee3f9579d6.js","canopyx-formatting.922dc9fc7e6d35a7.css"],"format-editor":["5398e457a885ef0d","canopyx-format-editor.f65fa4b3402ae488.js","canopyx-format-editor.dbdf4a154e7e5f48.css"],"link":["fd21dd4495607a16","canopyx-link.5874879aadd6cefb.js","canopyx-link.b872151e9cbbd0ce.css"],"detail-rows":["e4150bb67f40d597","canopyx-detail-rows.e7f93158e19cbb28.js","canopyx-detail-rows.cabc50f51ed091ad.css"],"calculations":["f902dc5856fc7bdd","canopyx-calculations.97e438237ba43f99.js","canopyx-calculations.50432e524dd7d8d9.css"],"pivot":["230b05b7fcab6cf1","canopyx-pivot.de89c7750bbaf133.js","canopyx-pivot.3a7e122e33aaeab4.css"],"values":["0d7006b0f7bfca10","canopyx-values.ad4c56fcbcd89cbb.js","canopyx-values.446654b98c62ef1d.css"],"buckets":["cc69c8336c15df83","canopyx-buckets.1f59613f13ee292c.js"],"grouping":["c7eead770d16a84b","canopyx-grouping.dcab87ec8593eb59.js","canopyx-grouping.c4dace7c55dbb84b.css"],"highlight":["459b28e174406d32","canopyx-highlight.10ef4bb28c5a8f5b.js","canopyx-highlight.ab1040f93cbb5dbb.css"],"layouts":["077268d4fb78c464","canopyx-layouts.9a318c748cde4c84.js","canopyx-layouts.18dbf5842f1c5efd.css"],"facets":["f1ed74d2447e4f11","canopyx-facets.9a51d843e93278f4.js","canopyx-facets.6da87c205c61cbe2.css"],"live":["4ef2fb01714f877e","canopyx-live.c6d5b70f326f3bf9.js","canopyx-live.b60435092089b07f.css"],"record-panel":["2a4059ade4ca1439","canopyx-record-panel.4f525c01ecbd0e07.js","canopyx-record-panel.f1d740569605e625.css"],"calendar":["4762fd2f9e9d1617","canopyx-calendar.df3bcff81a62548a.js","canopyx-calendar.88ef06b86bccd0df.css"],"calendar-recurrence":["64bcc24d785bd1af","canopyx-calendar-recurrence.4b7f430c2a64de23.js"],"calendar-edit":["09747e191820d94c","canopyx-calendar-edit.174f5125d3e75bd9.js"],"calendar-ics":["ac8b2950fccbfb0f","canopyx-calendar-ics.4a18c1c4a1520418.js"],"timeline":["ece982b3b7277183","canopyx-timeline.66fe4cefa5f41ff7.js","canopyx-timeline.e3c962ae517931d2.css"],"grid-options":["e74170bbb841519c","canopyx-grid-options.c5fa91fc19205d5a.js","canopyx-grid-options.9c393c5122332461.css"],"board":["31d8984cbdc39866","canopyx-board.5dcac9033d72968c.js","canopyx-board.2351154bbc60bf43.css"],"plan":["640ca4be173c3310","canopyx-plan.58418c1ff7605fa3.js","canopyx-plan.be9f8826b6ab669f.css"],"path":["28b4b60b1851bf2e","canopyx-path.e32fe5c520a77be5.js","canopyx-path.61905ad5e56e2fd0.css"],"stack":["9ebd0cfb3f4f074b","canopyx-stack.041611f7810280dd.js","canopyx-stack.c875bfcb7eeaea25.css"],"time-slider":["a4888d801922c858","canopyx-time-slider.687623fbee479b03.js","canopyx-time-slider.2a79dfc4921df0bb.css"],"view-intents":["26b4233395638945","canopyx-view-intents.9579957e593d30d4.js","canopyx-view-intents.5b6d00ea612494a7.css"],"compare":["2df5f137f630380f","canopyx-compare.1863f941f9f5207d.js","canopyx-compare.acd8641545cac008.css"]},PART_CODE=new Map(),SHARED_PARTS={},partName=name=>typeof name==="string"&&/^[a-z][a-z\d-]{0,31}$/.test(name);
|
|
529
534
|
function registerPart(name,part){if(!partName(name)||!part||typeof part.version!=="string"||typeof part.install!=="function"||
|
|
530
535
|
part.requires!==undefined&&!(Array.isArray(part.requires)&&part.requires.every(partName)))throw new Error("CanopyX: invalid part.");PART_CODE.set(name,part)}
|
|
531
536
|
function definePart(View,name,label,context){const parts=View.prototype.parts||={};return parts[name]={name,label,context,parts}}
|
|
@@ -534,8 +539,8 @@ script.textContent=trusted("Script",payload.textContent);script.nonce=payload.no
|
|
|
534
539
|
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")
|
|
535
540
|
.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")
|
|
536
541
|
.finally(()=>script.remove()).catch(error=>{part.scriptReady=null;throw error}));await Promise.all([style,code])}}
|
|
537
|
-
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})}
|
|
538
|
-
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,
|
|
542
|
+
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]||SHARED_PARTS[name]);code.install(part.context())}).catch(error=>{part.ready=null;throw error})}
|
|
543
|
+
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,INDENT,MAX_PROTOCOL_INTEGER,hostControl,detailCell,HostCellContext,disposeHostSlot,loadPart,definePart,pluginItems,applyTone,toneOf,badgeElement,RECORD_IMPL,svgIcon,gridExportField,columnNumericTone,columnValueTone,overlayTheme,UI_IMPL,UI_TOOLS}));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]:SHARED_PARTS[name];if(!part)throw new Error("CanopyX: unknown part '"+name+"'.");return loadPart(part)}
|
|
539
544
|
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")
|
|
540
545
|
throw new Error("CanopyX: invalid state column.");seen.add(c.id);return{id:c.id,width:c.width,hidden:c.hidden}}
|
|
541
546
|
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})})}
|
|
@@ -586,7 +591,12 @@ w.content=nodeContent(w.h)});this.inlineOrderVersion=(this.inlineOrderVersion||0
|
|
|
586
591
|
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})}}
|
|
587
592
|
const navigation=navigationScope(this,this.dataLifetime.signal);let doc;try{doc=await navigation.wait(this.source.counts(wanted,this.sourceRequest(this.dataLifetime.signal)))}
|
|
588
593
|
finally{navigation.dispose()}
|
|
589
|
-
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}});
|
|
594
|
+
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}});function UiHost(){}
|
|
595
|
+
const UI_IMPL={},UI_TOOLS={},uiInstances=new WeakMap();const UI_KINDS={tabs:"ui-tabs",stepper:"ui-stepper",menu:"ui-menu",dialog:"ui-overlays",drawer:"ui-overlays",popover:"ui-overlays",tooltip:"ui-overlays"};const uiContext=()=>({UI_IMPL,UI_TOOLS,h,setText,fill,watchTheme,overlayTheme,hostHtml});for(const name of["ui-overlays","ui-menu","ui-picker","ui-navigation","ui-tabs","ui-stepper"])SHARED_PARTS[name]=definePart(UiHost,name,name.replace("ui-","UI "),uiContext);function createUi(kind,target,options={}){const root=typeof target==="string"?document.querySelector(target):target;if(root?.nodeType!==1)throw new Error("CanopyX: "+kind+" needs an element.");let registrations=uiInstances.get(root);if(!registrations)uiInstances.set(root,registrations=new Map());if(registrations.has(kind))return registrations.get(kind);const lifetime=new AbortController();watchTheme(root,lifetime.signal);let instance=null;const ready=loadPart(SHARED_PARTS[UI_KINDS[kind]]).then(()=>{if(lifetime.signal.aborted)throw new DOMException("UI component destroyed.","AbortError");instance=UI_IMPL[kind](root,options,lifetime.signal);return instance.ready||instance});const api={element:root,ready:ready.then(()=>api),destroy(){if(lifetime.signal.aborted)return;lifetime.abort();instance?.destroy?.();registrations.delete(kind)}};for(const name of["select","getSelected","refresh","preparePrint","open","close","next","back","skip","finish","setStepState","getState"])api[name]=(...args)=>{if(lifetime.signal.aborted)throw new Error("CanopyX: UI component destroyed.");return instance?instance[name]?.(...args):ready.then(()=>instance[name]?.(...args))};api.ready.catch(error=>{if(error.name!=="AbortError")root.dispatchEvent(new CustomEvent("canopyx:error",{detail:{error},bubbles:true}))});registrations.set(kind,Object.freeze(api));return api}
|
|
596
|
+
function bindContextMenu(target,items,options={}){if(typeof target!=="string"&&target?.nodeType!==1)throw new Error("CanopyX: contextMenu needs an element or selector.");if(typeof target==="string")document.documentElement.matches(target);let disposed=false,cleanup=null;const dispose=()=>{disposed=true;cleanup?.()};dispose.ready=loadPart(SHARED_PARTS["ui-menu"]).then(()=>{if(!disposed)cleanup=UI_IMPL.contextMenu(target,items,options)});dispose.open=(...args)=>disposed?false:cleanup?cleanup.open(...args):dispose.ready.then(()=>!disposed&&cleanup.open(...args));dispose.close=(...args)=>cleanup?.close(...args);return dispose}
|
|
597
|
+
function mountUi(root=document){const mounted=[];for(const kind of Object.keys(UI_KINDS)){const selector="[data-cx-"+kind+"]",nodes=[...root.querySelectorAll(selector)];if(root.matches?.(selector))nodes.unshift(root);for(const element of nodes){if(kind==="menu"&&element.parentElement?.closest(selector))continue;if(kind==="stepper"&&["report","static"].includes(element.dataset.mode))continue;mounted.push(createUi(kind,element))}}
|
|
598
|
+
return mounted}
|
|
599
|
+
const UI_API={mountUi,contextMenu:bindContextMenu};for(const kind of Object.keys(UI_KINDS))UI_API[kind]=(target,options)=>createUi(kind,target,options);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.
|
|
590
600
|
}
|
|
591
601
|
this.linkChanged=this.dataLifetime.signal;return this.ready.then(()=>undefined,()=>undefined)}
|
|
592
602
|
return this.openDeepLink()};function sharedDensity(host){const value=host.closest("[data-hfx-density]")?.getAttribute("data-hfx-density");return value==="compact"||value==="comfortable"?value:null}
|
|
@@ -607,7 +617,7 @@ DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{linkFilteredBy:"Fi
|
|
|
607
617
|
linkMethods(TreeGrid);function linkGridPart(View){(Object.hasOwn(View.prototype,"parts")?View.prototype.parts:View.prototype.parts={}).link=LINK_PART}
|
|
608
618
|
function linkReady(grid,ready){return Promise.all([ready,grid.linkStart()]).then(([result])=>result)}
|
|
609
619
|
function linkApi(grid){const api={};for(const name of LINK_METHODS.concat("getExternalFilters","getHighlight"))api[name]=(...args)=>grid[name](...args);return api}
|
|
610
|
-
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,columnNumericTone,columnValueTone,validateColumnValues}));function loadRecordPanel(){return loadPart(RECORD_PART)}
|
|
620
|
+
const RECORD_IMPL={},RECORD_PART=definePart(TreeGrid,"record-panel","record panel",()=>({RECORD_IMPL,UI_IMPL,TreeGrid,h,setText,fill,cellText,cellValue,isCellObject,formatValue,toneOf,applyTone,toneColors,badgeElement,loadSidecar,requireIdentifier,validateGridRows,PROTOCOL,foldText,createTreeGrid,svgIcon,gridExportField,columnNumericTone,columnValueTone,validateColumnValues}));function loadRecordPanel(){return loadPart(RECORD_PART)}
|
|
611
621
|
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}))}
|
|
612
622
|
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:-8,default:0,comfortable:0},grid:{compact:-8,default:0,comfortable:0}};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");
|
|
613
623
|
if(!options||typeof options!=="object"||Array.isArray(options)||Object.keys(options).some(key=>!HIGHLIGHT_KEYS.includes(key))||
|
|
@@ -629,16 +639,21 @@ function densityRowHeight(view,density){const shared=sharedDensity(view.host);if
|
|
|
629
639
|
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});watchSharedDensity(this)},adoptModes(choice){if(choice.layout){this.layoutMode=choice.layout.mode;this.density=choice.layout.density}
|
|
630
640
|
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))
|
|
631
641
|
this.setLayout(state.layout).catch(error=>this.emit("error",{error}));if(state.highlight!==undefined&&state.highlight!==!!this.highlightOn)
|
|
632
|
-
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))
|
|
633
|
-
.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,toneVariable,watchFlyout,densityRowHeight,layoutAnchor,layoutRows,layoutApplyTree,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()}});
|
|
642
|
+
this.setSearchHighlight(state.highlight).catch(error=>this.emit("error",{error}))},startViewModes(){const parts=[this.highlightOptions&&"highlight",(this.layoutOptions||this.view.stickyHeader)&&"layouts"].filter(Boolean);return Promise.all(parts.map(name=>this.requirePart(name).then(()=>this.destroyed||VIEW_MODES[name+"Start"](this))
|
|
643
|
+
.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,toneVariable,watchFlyout,densityRowHeight,layoutAnchor,layoutRows,layoutApplyTree,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()}});function changeCellText(cell,column,fmt){if(cell==null)return "—";const structured=isCellObject(cell)&&["items","fields","records","events","changes"].some(name=>cell[name]);if(isCellObject(cell)&&cellValue(cell)==null&&!cell.text&&!structured)return "null";const text=cellText(cell,column,fmt);return text.length?text:'""'}
|
|
644
|
+
const ROW_HELPERS={};const GRID_REPORTING={};for(const name of["buildHeader","visibleColumns","applyWidths","setColumnWidth","startResize","cell","newCell","paintSlot","paintBadges","cellTip","paintHostSlot","setSlotContent","setTimeZone","rowCells","heldCell","restoreCell","focusCell","revealInRow","revealInCell","settleCell"])
|
|
634
645
|
ROW_HELPERS[name]=TreeGrid.prototype[name];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}
|
|
635
646
|
function gridDateInputMoment(text,local){const match=/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{1,3}))?)?$/.exec(text);
|
|
636
647
|
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}
|
|
637
648
|
function gridFreeze(value){if(value&&typeof value==="object"&&!Object.isFrozen(value)){Object.values(value).forEach(gridFreeze);Object.freeze(value)}
|
|
638
649
|
return value}
|
|
639
|
-
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",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)}
|
|
650
|
+
DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridPivot:"Pivot",gridPivotFieldOptions:"Field options",gridPivotRows:"Row groups",gridPivotColumns:"Column groups",gridPivotValues:"Values",gridPivotFlat:"Show records",gridPivotConfigure:"Pivot settings",gridPivotOther:"Other",gridPivotLimited:"Showing {shown} of {total} column paths; the rest are combined in Other. Subtotals include all matching descendants.",gridPivotColumn:"Choose column",gridPivotTopN:"Top columns",gridPivotSort:"Row order",gridPivotDrill:"Show records for {row}, {column}",gridPivotReturn:"Return to pivot",gridPivotInvalid:"Choose valid pivot axes and at least one value.",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",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)}
|
|
640
651
|
function gridDecorateButton(button,icon,ghost){button.classList.add("cx-btn");if(ghost)button.classList.add("ghost");if(icon)button.prepend(gridIcon(icon));return button}
|
|
641
|
-
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)}
|
|
652
|
+
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)+(query?.tupleFilters?.length||0)}
|
|
653
|
+
const GRID_BUCKETS={},GRID_BUCKET_KINDS=["day","week","month","quarter","year","hour","weekday","width","edges"];function gridBucketDefinition(input,column){const fail=()=>{throw new Error("CanopyX: invalid calendar or numeric bucket.")};if(!input||typeof input!=="object"||Array.isArray(input)||!GRID_BUCKET_KINDS.includes(input.kind))fail();const numeric=["width","edges"].includes(input.kind),fields=numeric?input.kind==="width"?["kind","width","origin"]:["kind","edges"]:["kind","timeZone","firstWeekday"];if(Object.keys(input).some(key=>!fields.includes(key))||column.kind!==(numeric?"number":"datetime"))fail();if(input.kind==="width"){if(!Number.isFinite(input.width)||input.width<=0||input.origin!==undefined&&!Number.isFinite(input.origin))fail();return gridFreeze({kind:input.kind,width:input.width,origin:input.origin??0})}
|
|
654
|
+
if(input.kind==="edges"){if(!Array.isArray(input.edges)||!input.edges.length||input.edges.length>200||input.edges.some((edge,index)=>!Number.isFinite(edge)||index&&edge<=input.edges[index-1]))fail();return gridFreeze({kind:input.kind,edges:[...input.edges]})}
|
|
655
|
+
const timeZone=input.timeZone??"UTC",firstWeekday=input.firstWeekday??1;if(typeof timeZone!=="string"||!timeZone.length||timeZone.length>128||!Number.isInteger(firstWeekday)||firstWeekday<0||firstWeekday>6)fail();try{new Intl.DateTimeFormat("en",{timeZone})}catch(_){fail()}
|
|
656
|
+
return gridFreeze({kind:input.kind,timeZone,firstWeekday})}
|
|
642
657
|
const GRID_OPTIONS={},GRID_OPTION_GRIDS=new WeakMap();const gridClassName=value=>typeof value==="string"&&value.length<=512&&/^(?:[A-Za-z_][\w-]*)(?: +[A-Za-z_][\w-]*)*$/.test(value);
|
|
643
658
|
function gridColumnOptions(column,columns){if(column.className!=null&&!gridClassName(column.className)||
|
|
644
659
|
column.alignment!=null&&!["start","center","end"].includes(column.alignment)||
|
|
@@ -660,27 +675,36 @@ for(const key of keys)if(defaults[key]===undefined&&rule[key]!=null)defaults[key
|
|
|
660
675
|
function gridOptionsUsed(view,plugins=[]){return!!(view.footers?.length||view.columns.some(column=>column.orderData||column.sortComparator)||
|
|
661
676
|
plugins.some(plugin=>plugin.filterOperators||plugin.sortComparators))}
|
|
662
677
|
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)}
|
|
663
|
-
function gridPredicate(filter,columns,budget){if(++budget.predicates>64)throw new Error("CanopyX: at most 64 grid predicates.");
|
|
664
|
-
!(Object.hasOwn(GRID_FILTER_LABELS,filter.operator)
|
|
678
|
+
function gridPredicate(filter,columns,budget){if(++budget.predicates>64)throw new Error("CanopyX: at most 64 grid predicates.");let column=filter&&columns.get(filter.column);const values=filter&&filter.values,bucket=filter?.bucket===undefined?null:gridBucketDefinition(filter.bucket,column||{});if(bucket)column={...column,kind:"number"};if(!column||budget.filterable&&column.filterable===false||typeof filter.operator!=="string"||
|
|
679
|
+
!(Object.hasOwn(GRID_FILTER_LABELS,filter.operator)||!bucket&&/^[a-z][a-zA-Z\d]{0,31}$/.test(filter.operator)) ||
|
|
665
680
|
!Array.isArray(values))
|
|
666
681
|
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)
|
|
667
682
|
throw new Error("CanopyX: invalid grid predicate arity.");if(["contains","startsWith"].includes(filter.operator)&&(column.kind!=="text"||values.some(value=>typeof value!=="string"))||
|
|
668
683
|
["gt","ge","lt","le"].includes(filter.operator)&&!["number","datetime"].includes(column.kind))
|
|
669
|
-
throw new Error("CanopyX: predicate does not match the column kind.");values.forEach(value=>{if(value==null||typeof value==="object")throw new Error("CanopyX: predicate operands are non-null raw values.");gridScalar(value,column)});return gridQueryRecord(filter,{column:column.id,operator:filter.operator,values:values.slice()},budget.strict)}
|
|
684
|
+
throw new Error("CanopyX: predicate does not match the column kind.");values.forEach(value=>{if(value==null||typeof value==="object")throw new Error("CanopyX: predicate operands are non-null raw values.");gridScalar(value,column)});return gridQueryRecord(filter,{column:column.id,operator:filter.operator,values:values.slice(),...(bucket?{bucket}:{})},budget.strict)}
|
|
670
685
|
function gridFilterGroup(group,columns,budget,depth){if(depth>8||++budget.groups>64)throw new Error("CanopyX: grid filter groups exceed their depth or count limit.");if(!group||!["and","or"].includes(group.operator)||!Array.isArray(group.filters)||!Array.isArray(group.groups)||
|
|
671
686
|
!group.filters.length&&!group.groups.length)
|
|
672
687
|
throw new Error("CanopyX: invalid or empty grid filter group.");const filters=group.filters.map(filter=>gridPredicate(filter,columns,budget));const groups=group.groups.map(child=>gridFilterGroup(child,columns,budget,depth+1));return gridQueryRecord(group,{operator:group.operator,filters,groups},budget.strict)}
|
|
673
688
|
function gridMatchesGroup(row,group,columns,matches=gridMatches){if(!group)return true;if(group.operator==="and")return group.filters.every(filter=>matches(row,filter,columns.get(filter.column)))&&
|
|
674
689
|
group.groups.every(child=>gridMatchesGroup(row,child,columns,matches));return group.filters.some(filter=>matches(row,filter,columns.get(filter.column)))||
|
|
675
690
|
group.groups.some(child=>gridMatchesGroup(row,child,columns,matches))}
|
|
676
|
-
function gridSearchTerms(text,mode){if(mode!=="words")return text?[foldText(text)]:[];return[...text.matchAll(/"([^"]*)(?:"|$)|([^\s"]+)/g)].map(match => foldText(match[1] === undefined ? match[2] : match[1])).filter(Boolean);
|
|
691
|
+
function gridSearchTerms(text,mode){if(mode==="regex")return text?[{pattern:boundedPattern(text,false),deadline:performance.now()+ 50}]:[];if(mode!=="words")return text?[foldText(text)]:[];return[...text.matchAll(/"([^"]*)(?:"|$)|([^\s"]+)/g)].map(match => foldText(match[1] === undefined ? match[2] : match[1])).filter(Boolean);
|
|
677
692
|
}
|
|
678
|
-
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);
|
|
693
|
+
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);if(term.pattern){if(performance.now()>term.deadline)throw new Error("CanopyX: regex search exceeded its 50 ms evaluation budget.");const input=text==null?"":String(text);if(input.length>4096)throw new Error("CanopyX: regex search cell input exceeds 4,096 characters.");return term.pattern.test(input)}
|
|
694
|
+
return foldText(text).includes(term)}))}
|
|
695
|
+
function gridTupleFilters(input,columns,strict){if(!Array.isArray(input)||input.length>8)throw new Error("CanopyX: at most eight tuple predicates.");return input.map(tuple=>{if(!tuple||typeof tuple!=="object"||Array.isArray(tuple)||!Array.isArray(tuple.columns)||
|
|
696
|
+
tuple.columns.length<1||tuple.columns.length>6||!Array.isArray(tuple.values)||
|
|
697
|
+
tuple.values.length<1||tuple.values.length>200||tuple.exclude!==undefined&&typeof tuple.exclude!=="boolean")
|
|
698
|
+
throw new Error("CanopyX: invalid tuple predicate.");const fields=tuple.columns.map(field=>{const column=field&&columns.get(field.column);if(!field||typeof field!=="object"||Array.isArray(field)||!column||column.filterable===false)
|
|
699
|
+
throw new Error("CanopyX: tuple fields require filterable columns.");const bucket=field.bucket===undefined?null:gridBucketDefinition(field.bucket,column);return gridQueryRecord(field,{column:column.id,...(bucket?{bucket}:{})},strict)});const values=tuple.values.map(row=>{if(!Array.isArray(row)||row.length!==fields.length)throw new Error("CanopyX: tuple widths must match their fields.");return Array.from(row,(value,index)=>{if(value!==null){if(value===undefined||typeof value==="object")throw new Error("CanopyX: tuple operands are raw scalars or explicit null.");const column=columns.get(fields[index].column);gridScalar(value,fields[index].bucket?{...column,kind:"number"}:column)}
|
|
700
|
+
return value})});return gridQueryRecord(tuple,{columns:fields,values,exclude:tuple.exclude??false},strict)})}
|
|
701
|
+
function gridMatchesTuples(row,tuples,columns){return!tuples||tuples.every(tuple=>{const scalars=tuple.columns.map(field=>{const scalar=gridScalar(row.cells[field.column],columns.get(field.column));return field.bucket?GRID_BUCKETS.key(field.bucket,scalar):scalar});const member=tuple.values.some(values=>values.every((value,index)=>{const field=tuple.columns[index],column=columns.get(field.column);return scalars[index]===(value===null||field.bucket?value:gridScalar(value,column))}));return tuple.exclude?!member:member})}
|
|
679
702
|
function gridQueryStart(query,patch){return{...query,...patch,offset:0,cursor:undefined,revision:undefined}}
|
|
680
703
|
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||
|
|
681
704
|
Object.keys(format).some(key=>!["minimumFractionDigits","maximumFractionDigits","prefix","suffix"].includes(key))||
|
|
682
705
|
[format.prefix,format.suffix].some(text=>text!==undefined&&(typeof text!=="string"||!text||text.length>16)))throw new Error("CanopyX: invalid number format.")}
|
|
683
|
-
function validateGridView(view){checkDocument(view,"grid");requireIdentifier(view.id,"grid ID");
|
|
706
|
+
function validateGridView(view){checkDocument(view,"grid");requireIdentifier(view.id,"grid ID");if([view.board,view.path,view.stack,view.calendar].filter(value=>value!=null).length>1)
|
|
707
|
+
throw new Error("CanopyX: choose one grid presentation: board, path, stack or calendar (with optional plan).");view=gridColumnDefaults(view);if(view.allowRegexSearch!==undefined&&typeof view.allowRegexSearch!=="boolean")throw new Error("CanopyX: allowRegexSearch is a boolean.");if(view.rowIdentity!==undefined&&!["stableKey","ordinal"].includes(view.rowIdentity))
|
|
684
708
|
throw new Error("CanopyX: rowIdentity must be stableKey or ordinal.");if(view.rowIdentity==="ordinal"&&view.live)
|
|
685
709
|
throw new Error("CanopyX: ordinal row identity is snapshot-local; keyed live updates require stable row keys.");if(view.rowIdentity==="ordinal"&&view.record?.deepLink)
|
|
686
710
|
throw new Error("CanopyX: ordinal row identity is snapshot-local; record deep links require stable row keys.");if(typeof view.label!=="string"||!view.label.trim()||!Array.isArray(view.columns)||!view.columns.length)
|
|
@@ -692,7 +716,8 @@ column.width!==undefined&&(!Number.isInteger(column.width)||column.width<40)||
|
|
|
692
716
|
column.flex!==undefined&&!(typeof column.flex==="number"&&column.flex>=0&&column.flex<=100)||column.autoSize!==undefined&&!["content","header","none"].includes(column.autoSize)||
|
|
693
717
|
column.pinned!==undefined&&![null,"left","right"].includes(column.pinned)||column.priority!==undefined&&!gridPriority(column.priority)||
|
|
694
718
|
column.fixed&&column.width===undefined||column.tooltip!==undefined&&column.tooltip!=="auto"&&!view.columns.some(item=>item.id===column.tooltip))
|
|
695
|
-
throw new Error("CanopyX: invalid or duplicate grid column.");gridColumnOptions(column,view.columns);ids.add(column.id);
|
|
719
|
+
throw new Error("CanopyX: invalid or duplicate grid column.");gridColumnOptions(column,view.columns);ids.add(column.id);if(column.calculation!==undefined&&(typeof column.calculation!=="string"||!column.calculation.trim()||column.calculation.length>2048||column.editable))
|
|
720
|
+
throw new Error("CanopyX: calculated columns need a bounded expression and cannot be editable.");validateColumnValues(column);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)}
|
|
696
721
|
if(column.aggregatable!==undefined&&(typeof column.aggregatable!=="boolean"||column.aggregatable&&column.kind!=="number")||
|
|
697
722
|
column.section!==undefined&&(typeof column.section!=="string"||!column.section.trim()||column.section.length>256))
|
|
698
723
|
throw new Error("CanopyX: invalid column aggregate or drawer section.")}
|
|
@@ -708,29 +733,42 @@ throw new Error("CanopyX: search columns must identify unique declared columns."
|
|
|
708
733
|
view.rowHeight!==undefined&&view.rowHeight!=="auto"&&(!Number.isInteger(view.rowHeight)||view.rowHeight<20||view.rowHeight>64)||
|
|
709
734
|
view.estimatedRowHeight!==undefined&&(!Number.isInteger(view.estimatedRowHeight)||view.estimatedRowHeight<20||view.estimatedRowHeight>64)||
|
|
710
735
|
!Number.isInteger(view.pageSize)||view.pageSize<1||view.pageSize>10000||
|
|
711
|
-
!["none","single","multiple"].includes(view.selection))throw new Error("CanopyX: invalid grid sizing or selection.");if(!view.source||!["inline","host"].includes(view.source.kind))throw new Error("CanopyX: grid source must be inline or host.");if(view.
|
|
736
|
+
!["none","single","multiple"].includes(view.selection))throw new Error("CanopyX: invalid grid sizing or selection.");if(!view.source||!["inline","host"].includes(view.source.kind))throw new Error("CanopyX: grid source must be inline or host.");if(view.calculatedColumnsEditable!==undefined&&(typeof view.calculatedColumnsEditable!=="boolean"||view.calculatedColumnsEditable&&view.source.kind!=="inline"))
|
|
737
|
+
throw new Error("CanopyX: the calculated-column editor requires an inline grid.");if(view.source.navigation!==undefined&&!["offset","cursor"].includes(view.source.navigation))
|
|
712
738
|
throw new Error("CanopyX: invalid grid source navigation.");if(view.source.navigation==="cursor"&&(view.source.kind!=="host"||view.presentation!=="paged"))
|
|
713
739
|
throw new Error("CanopyX: cursors need a hosted paged grid.");if(view.presentation!==undefined&&!["continuous","paged"].includes(view.presentation)||
|
|
714
740
|
view.pageLength!==undefined&&(!Number.isInteger(view.pageLength)||view.pageLength<1||view.pageLength>10000))
|
|
715
|
-
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.")}
|
|
716
|
-
|
|
741
|
+
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.")}
|
|
742
|
+
const rows=view.rowData===undefined&&view.source.kind==="inline"?validateGridRows(view.rows,view.columns,undefined,repairGridCell):view.rows;if(view.source.kind!=="inline"&&view.rows!==undefined)throw new Error("CanopyX: host grids cannot discard inline rows.");if(view.query!==undefined)gridQuery(view,view.query);return{...view,...(rows===undefined?{}:{rows}),rowHeight:view.rowHeight??(view.density==="compact"?32:40)}}
|
|
743
|
+
function gridQuery(view,input,strict){input=input||{};if(Array.isArray(input.tupleFilters)&&!input.tupleFilters.length){input={...input};delete input.tupleFilters}
|
|
744
|
+
const cursor=view.source.navigation==="cursor",type=cursor?"gridCursorQuery":"gridQuery";if(typeof input!=="object"||Array.isArray(input)||
|
|
717
745
|
input.protocol!==undefined&&input.protocol!==PROTOCOL||input.type!==undefined&&input.type!==type||
|
|
718
746
|
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)
|
|
719
747
|
throw new Error("CanopyX: cursor and offset navigation cannot be mixed.");if(!cursor)query.offset=input.offset===undefined?0:input.offset;if(!cursor&&!validStructuralCount(query.offset)||!Number.isInteger(query.limit)||query.limit<1||query.limit>10000||typeof query.search!=="string")
|
|
720
748
|
throw new Error("CanopyX: invalid grid query window or search.");if(input.revision!==undefined)requireIdentifier(input.revision,"grid revision");if(input.cursor!==undefined){gridCursor(input.cursor);if(!query.revision)throw new Error("CanopyX: a cursor requires its dataset revision.")}
|
|
721
|
-
if(input.searchMode!==undefined&&!["substring","words"].includes(input.searchMode))throw new Error("CanopyX: invalid grid search mode.");
|
|
749
|
+
if(input.searchMode!==undefined&&!["substring","words","regex"].includes(input.searchMode))throw new Error("CanopyX: invalid grid search mode.");if(query.searchMode==="regex"){if(!view.allowRegexSearch)throw new Error("CanopyX: this grid does not allow regex search.");if(query.search)boundedPattern(query.search,false)}
|
|
750
|
+
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))
|
|
722
751
|
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)||
|
|
723
752
|
!["asc","desc"].includes(sort.direction))
|
|
724
753
|
throw new Error("CanopyX: invalid grid sort column or direction.");sorts.add(sort.column);return gridQueryRecord(sort,{column:sort.column,direction:sort.direction},strict)});if(view.preSort!==undefined){if(!Array.isArray(view.preSort)||view.preSort.length>32)throw new Error("CanopyX: invalid fixed pre-sort.");const fixed=new Set();for(const sort of view.preSort){if(!columns.get(sort?.column)?.sortable||!["asc","desc"].includes(sort.direction)||fixed.has(sort.column))throw new Error("CanopyX: invalid fixed pre-sort.");fixed.add(sort.column)}
|
|
725
754
|
query.sort=[...view.preSort.map(sort=>({column:sort.column,direction:sort.direction})),...query.sort.filter(sort=>!fixed.has(sort.column))]}
|
|
726
|
-
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)}
|
|
755
|
+
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.tupleFilters!==undefined)query.tupleFilters=gridTupleFilters(input.tupleFilters,columns,strict);if(input[LINK_IMPL.queryOwn])query[LINK_IMPL.queryOwn]=true;return gridQueryRecord(input,query,strict,cursor)}
|
|
727
756
|
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)||
|
|
728
757
|
page.matched>page.total||page.offset>page.matched||!Array.isArray(page.items)||
|
|
729
758
|
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)}
|
|
730
759
|
function checkedGridPage(page,view,query,accepted){if(query.revision!==undefined&&page.revision!==query.revision||accepted&&
|
|
731
760
|
(page.revision!==accepted.revision||accepted.total!==undefined&&page.total!==accepted.total||
|
|
732
|
-
accepted.matched!==undefined&&page.matched!==accepted.matched))throw new Error("CanopyX: grid page snapshot changed.");validateGridRows(page.items,view.columns);return{...page,items
|
|
761
|
+
accepted.matched!==undefined&&page.matched!==accepted.matched))throw new Error("CanopyX: grid page snapshot changed.");const items=JSON.parse(JSON.stringify(validateGridRows(page.items,view.columns,undefined,repairGridCell)));return{...page,items}}
|
|
762
|
+
function repairGridCell(cell,column){const raw=cellValue(cell),shown=typeof raw==="string"||typeof raw==="boolean"?String(raw):undefined;let value=raw;try{gridScalar(value,column)}catch{value=null;if(column.kind==="number"&&typeof raw==="string"&&raw.trim())value=Number(raw);else if(column.kind!=="number"&&column.kind!=="datetime"&&(typeof raw==="bigint"||safeGridNumber(raw)))value=String(raw);try{gridScalar(value,column)}catch{value=null}}
|
|
763
|
+
const text=isCellObject(cell)&&!Array.isArray(cell)&&typeof cell.text==="string"?cell.text:value===null?shown:undefined;if(isCellObject(cell)&&!Array.isArray(cell)){const kept={...cell,value,...(text===undefined?{}:{text})};try{validateGridCell(kept,column);return kept}catch{}
|
|
764
|
+
delete kept.sort;try{validateGridCell(kept,column);return kept}catch{}}
|
|
765
|
+
return text===undefined?value:{value,text}}
|
|
766
|
+
function readCardTemplate(value,columns,layout){if(value===undefined)return;const ids=new Set(columns.map(column=>column.id));if(!layout?.modes.includes("cards")||!value||typeof value!=="object"||Array.isArray(value)||
|
|
767
|
+
!Object.keys(value).length||Object.keys(value).length>64)throw new Error("CanopyX: cardTemplate needs a card layout and 1 to 64 column slots.");for(const[id,template]of Object.entries(value)){if(!ids.has(id)||typeof template!=="string"||template.length>2048)throw new Error("CanopyX: invalid card template slot.");const literal=template.replace(/\{([^{}]+)\}/g, (_, column) => {
|
|
768
|
+
if(!ids.has(column))throw new Error("CanopyX: card template tokens name declared columns.");return ""});if(/[{}]/.test(literal)) throw new Error("CanopyX: unmatched card template token.");
|
|
769
|
+
}}
|
|
733
770
|
function gridOrder(left,right){return left===right?0:left<right?-1:1}
|
|
771
|
+
function gridInlineRevision(view){return GRID_CALCULATIONS.revision?.(view)||"inline"}
|
|
734
772
|
function gridIntegerId(id){if(id.length>1&&id.charCodeAt(0)===48)return false;for(let index=0;index<id.length;index++){const code=id.charCodeAt(index);if(code<48||code>57)return false}
|
|
735
773
|
return id.length>0}
|
|
736
774
|
function gridIdOrder(left,right){if(left===right)return 0;const a=gridIntegerId(left),b=gridIntegerId(right);if(a!==b)return a?-1:1;return a&&left.length!==right.length?left.length-right.length:left<right?-1:1}
|
|
@@ -738,13 +776,13 @@ function gridSortScalar(cell,column){return!column.valueOrder?.length&&isCellObj
|
|
|
738
776
|
function gridCompare(left,right,column,direction){return gridKeyCompare(gridSortScalar(left.cells[column.id],column),gridSortScalar(right.cells[column.id],column),column,direction)}
|
|
739
777
|
function gridKeyCompare(a,b,column,direction){if(a===null||b===null)return a===b?0:a===null?1:-1;if(column.valueOrder?.length){const at=value=>{const index=typeof value==="string"?column.valueOrder.indexOf(value):-1;return index<0?column.valueOrder.length:index};const rank=at(a)- at(b),result=rank||gridOrder(typeof a,typeof b)||gridOrder(a,b);return direction==="desc"?-result:result}
|
|
740
778
|
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}
|
|
741
|
-
function gridMatches(row,predicate,column){if(!Object.hasOwn(GRID_FILTER_LABELS,predicate.operator))throw new Error("CanopyX: no plugin provides filter operator '"+predicate.operator+"'.");return cellMatches(row.cells[column.id],predicate,column)}
|
|
742
|
-
function gridQueryRows(view,query,columns,compare=gridCompare,matches=gridMatches,extended=false){if(!extended&&GRID_OPTIONS.queryRows&&GRID_OPTION_GRIDS.has(view))return GRID_OPTIONS.queryRows(view,query,columns,compare);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)&&
|
|
743
|
-
query.filters.every(predicate=>matches(row,predicate,columns.get(predicate.column)))&&gridMatchesGroup(row,query.filterGroup,columns,matches));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}
|
|
779
|
+
function gridMatches(row,predicate,column){if(!Object.hasOwn(GRID_FILTER_LABELS,predicate.operator))throw new Error("CanopyX: no plugin provides filter operator '"+predicate.operator+"'.");if(predicate.bucket)return cellMatches(GRID_BUCKETS.key(predicate.bucket,gridScalar(row.cells[column.id],column)),predicate,{...column,kind:"number"});return cellMatches(row.cells[column.id],predicate,column)}
|
|
780
|
+
function gridQueryRows(view,query,columns,compare=gridCompare,matches=gridMatches,extended=false){GRID_CALCULATIONS.apply?.(view);if(!extended&&GRID_OPTIONS.queryRows&&GRID_OPTION_GRIDS.has(view))return GRID_OPTIONS.queryRows(view,query,columns,compare);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)&&
|
|
781
|
+
query.filters.every(predicate=>matches(row,predicate,columns.get(predicate.column)))&&gridMatchesGroup(row,query.filterGroup,columns,matches)&&gridMatchesTuples(row,query.tupleFilters,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}
|
|
744
782
|
return gridIdOrder(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}
|
|
745
783
|
return gridIdOrder(rows[a].id,rows[b].id)}).map(index=>rows[index])}
|
|
746
784
|
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}
|
|
747
|
-
return{protocol:PROTOCOL,type:"gridPage",view:view.id,revision:
|
|
785
|
+
return{protocol:PROTOCOL,type:"gridPage",view:view.id,revision:gridInlineRevision(view),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)}
|
|
748
786
|
return ordered.findIndex(row=>row.id===id)},row(id){return view.rows.find(row=>row.id===id)||null}}}
|
|
749
787
|
function gridCursor(cursor){requireIdentifier(cursor,"grid cursor");if(cursor.length>4096)throw new Error("CanopyX: cursor exceeds 4096 code units.")}
|
|
750
788
|
function validateGridCursorPage(page,view,query,accepted){checkDocument(page,"gridCursorPage");requireIdentifier(page.revision,"grid revision");if(page.view!==view.id||!validStructuralCount(page.offset)||
|
|
@@ -756,7 +794,7 @@ page.matched!==null&&next!==(end<page.matched)||page.total!==null&&next&&end>=pa
|
|
|
756
794
|
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)}
|
|
757
795
|
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:" +(gridExactTokens(column)?scalar:foldText(scalar)):
|
|
758
796
|
typeof scalar==="boolean"?"b:"+scalar:"n:"+scalar}
|
|
759
|
-
function validateGridFacetPage(input,view,request,snapshot){checkDocument(input,"gridFacetPage");const
|
|
797
|
+
function validateGridFacetPage(input,view,request,snapshot){checkDocument(input,"gridFacetPage");const declared=view.columns.find(item=>item.id===request.column),column=request.bucket?{...declared,kind:"number"}:declared;if(JSON.stringify(input.bucket)!==JSON.stringify(request.bucket)||input.view!==view.id||input.revision!==request.query.revision||input.column!==request.column||
|
|
760
798
|
input.search!==request.search||input.includeCurrentFilter!==request.includeCurrentFilter||
|
|
761
799
|
!Number.isInteger(input.matched)||input.matched<0||input.matched>MAX_PROTOCOL_INTEGER||
|
|
762
800
|
!Number.isInteger(input.totalValues)||input.totalValues<0||input.totalValues>input.matched||
|
|
@@ -767,7 +805,7 @@ gridResultShape(gridColumnProjection(request.query,request.column))!==gridResult
|
|
|
767
805
|
(widened?input.matched<snapshot.matched:input.matched!==snapshot.matched))
|
|
768
806
|
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)
|
|
769
807
|
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)
|
|
770
|
-
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})}
|
|
808
|
+
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,...(request.bucket?{bucket:request.bucket}:{})})}
|
|
771
809
|
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(this.view.rowIdentity==="ordinal"&&options.live)
|
|
772
810
|
throw new Error("CanopyX: ordinal row identity is snapshot-local; keyed live updates require stable row keys.");if(rows)this.view.rows=owned?rows:JSON.parse(JSON.stringify(rows));if(rowData){this.view.rowData=owned?rowData:structuredClone(rowData);this.view.rows=[]}
|
|
773
811
|
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"))
|
|
@@ -791,7 +829,9 @@ if(offset){const snapshot={revision:page.revision,total:page.total,matched:page.
|
|
|
791
829
|
gridOperationActive(this,lifetime.signal);const hadFocus=this.canvas.contains(document.activeElement),previousId=this.focused&&this.focused.key;const retireSelection=this.view.rowIdentity==="ordinal"&&this.snapshot&&this.snapshot.revision!==page.revision;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};if(retireSelection)this.clearSnapshotSelection();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}
|
|
792
830
|
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.focusOrigin){this.focused=[...this.rowCache.values()].find(row=>row.key===previousId)||this.rowCache.get(this.cursor?page.offset:query.offset)||null}
|
|
793
831
|
this.render();if(hadFocus){const element=this.focused&&this.rendered.get(this.focused);(element||this.grid).focus({preventScroll:true})}
|
|
794
|
-
this.host.setAttribute("data-cx-ready","true");if(this.searchInput&&!this.searchComposing&&this.searchTimer==null)this.searchInput.value=query.search;
|
|
832
|
+
this.host.setAttribute("data-cx-ready","true");if(this.searchInput&&!this.searchComposing&&this.searchTimer==null)this.searchInput.value=query.search;if(this.searchInput){const label=query.searchMode==="regex"?this.labels.gridRegexSearch:this.view.noun?
|
|
833
|
+
fill(this.labels.gridSearchNoun,{noun:this.noun}):this.labels.searchPlaceholder;this.searchInput.placeholder=label;this.searchInput.setAttribute("aria-label",label);this.searchInput.maxLength=query.searchMode==="regex"?256:524288;this.searchInput.setCustomValidity("")}
|
|
834
|
+
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){const cancelled=lifetime.signal.aborted&&error?.name==="AbortError";this.pendingQuery=null;this.pendingPaging=null;this.lastFailedQuery=cancelled?null:initial;this.lastFailedPaging=cancelled?null:{...paging,pageLength:length};this.dataLifetime.abort();this.dataLifetime=new AbortController();this.grid.setAttribute("aria-busy","false");this.setNotice(cancelled?"":fill(this.labels.queryFailed,{error:error.message}),!cancelled);this.syncPager();if(!cancelled)this.emit("error",{error})}
|
|
795
835
|
throw error}}
|
|
796
836
|
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 authored=this.columns.find(column=>column.id===id).orderSequence;const sequence=authored?.length?authored:["asc","desc","none"];const direction=sequence[(sequence.indexOf(old||"none")+ 1)%sequence.length];const next=direction==="none"?null:direction;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(()=>{})}
|
|
797
837
|
sort(sorts){return this.setQuery(gridQueryStart(this.queryForChange(true),{sort:sorts}))}
|
|
@@ -824,7 +864,7 @@ if(!this.rowCache.has(index))offsets.add(Math.floor(index / this.pageSize) * thi
|
|
|
824
864
|
if(this.focused?.status&&this.focused._ri>=0)offsets.add(Math.floor(this.focused._ri / this.pageSize) * this.pageSize);
|
|
825
865
|
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);
|
|
826
866
|
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))}
|
|
827
|
-
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])}
|
|
867
|
+
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.tupleFilters,query.sort])}
|
|
828
868
|
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:
|
|
829
869
|
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,
|
|
830
870
|
pageCount:this.paged?Math.ceil(count / this.pageLength) : count ? 1 : 0 } });
|
|
@@ -850,14 +890,15 @@ if(this.pendingNavigation!==navigation)return;if(this.destroyed||this.pendingQue
|
|
|
850
890
|
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&&
|
|
851
891
|
!this.destroyed&&!this.dataLifetime.signal.aborted&&epoch===this.queryEpoch&&!this.pendingQuery&&
|
|
852
892
|
this.pendingNavigation===navigation&&this.rowCache.get(index)===initial&&this.focused===initial&&
|
|
853
|
-
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");watchTheme(host,signal);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))
|
|
893
|
+
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");watchTheme(host,signal);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.view.autoHeight===false)grid.setAttribute("data-cx-finish-allow","empty-band");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))
|
|
854
894
|
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})}
|
|
855
895
|
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)
|
|
856
|
-
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;if(this.focusOrigin&&event.target!==this.focusOrigin)this.focusOrigin=null},{signal,capture:true});document.addEventListener("focusout",event=>{if(event.target===this.focusOrigin)this.focusOrigin=null},{signal,capture:true});this.canvas.addEventListener("focusout",event=>this.settleCell(event.relatedTarget),{signal});this.sized=this.hasGridSize();grid.style.opacity="0";let resizeFrame;signal.addEventListener("abort",()=>cancelAnimationFrame(resizeFrame),{once:true});this.resizeObserver=new ResizeObserver(entries=>{const size=entries.find(entry=>entry.target===grid);if(size)this.fitContentWidth=size.contentRect.width;if(size&&!size.contentRect.width)grid.style.opacity="0";const resize=()=>{if(this.destroyed)return;if(!grid.clientWidth||!grid.checkVisibility()){this.sized=false;return}
|
|
857
|
-
style.setProperty("--cx-min-h",(this.
|
|
858
|
-
height +(child===grid?0:child.offsetHeight),this.rowH*Math.min(4,Math.max(1,this.rowCount()))+ 2))+ "px");this.fitHeight();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)}
|
|
896
|
+
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;if(this.focusOrigin&&event.target!==this.focusOrigin)this.focusOrigin=null},{signal,capture:true});document.addEventListener("focusout",event=>{if(event.target===this.focusOrigin)this.focusOrigin=null},{signal,capture:true});this.canvas.addEventListener("focusout",event=>this.settleCell(event.relatedTarget),{signal});this.sized=this.hasGridSize();grid.style.opacity="0";let resizeFrame;signal.addEventListener("abort",()=>cancelAnimationFrame(resizeFrame),{once:true});this.resizeObserver=new ResizeObserver(entries=>{const size=entries.find(entry=>entry.target===grid);if(size)this.fitContentWidth=size.contentRect.width;if(size&&!size.contentRect.width)grid.style.opacity="0";const resize=()=>{if(this.destroyed)return;if(!grid.clientWidth||!grid.checkVisibility()){if(host.clientWidth&&host.checkVisibility())this.updateGridPhone();this.sized=false;return}
|
|
897
|
+
if(this.fitContentWidth===0)return;if(host.classList.contains("cx-grid-awaiting"))grid.style.setProperty("--cx-empty-head-height",Math.max(this.phone?44:0,this.head.offsetHeight+grid.offsetHeight-grid.clientHeight)+ "px");style.setProperty("--cx-min-h",this.minimumGridHeight()+ "px");this.fitHeight();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)}
|
|
859
898
|
if(grid.style.opacity){this.render();grid.style.opacity=""}
|
|
860
|
-
else this.scheduleRender()};cancelAnimationFrame(resizeFrame);resizeFrame=requestAnimationFrame(resize)});this.resizeObserver.observe(grid);this.resizeObserver.observe(this.head)},
|
|
899
|
+
else this.scheduleRender()};cancelAnimationFrame(resizeFrame);resizeFrame=requestAnimationFrame(resize)});this.resizeObserver.observe(grid);this.resizeObserver.observe(this.head);this.resizeObserver.observe(host)},minimumGridHeight(){if(this.toolbar.hidden)return 0;return[...this.host.children].reduce((height,child)=>{if(child===this.grid)return height;const css=getComputedStyle(child);if(css.position==="absolute"||css.position==="fixed")return height;let block=child.offsetHeight;if(child===this.notice&&child.classList.contains("cx-state")){const items=[...child.children];block=(items.length?items.at(-1).getBoundingClientRect().bottom-items[0].getBoundingClientRect().top:0)+
|
|
900
|
+
parseFloat(css.paddingTop)+ parseFloat(css.paddingBottom)}
|
|
901
|
+
return height+block},Math.max(this.head.offsetHeight,this.rowH*Math.min(4,Math.max(1,this.rowCount())))+ 2)},setLogicalTop(top){if(!this.hasGridSize()){this.hiddenScrollTop=top;return}
|
|
861
902
|
const result=ROW_SCROLLING.setLogicalTop.call(this,top);this.visibleScrollTop=ROW_SCROLLING.logicalTop.call(this);return result},logicalTop(){if(this.hiddenScrollTop!==undefined)return this.hiddenScrollTop;if(!this.grid.checkVisibility())return this.visibleScrollTop??0;return this.visibleScrollTop=ROW_SCROLLING.logicalTop.call(this)},hasGridSize(){return!!(this.grid.clientWidth&&this.grid.clientHeight&&this.grid.checkVisibility())},buildHeader(){const fixed=new Set((this.view.preSort||[]).map(sort=>sort.column));const sorts=(this.acceptedQuery?.sort||[]).filter(sort=>!fixed.has(sort.column));const fixedText=(this.view.preSort||[]).map(sort=>(this.columns.find(column=>column.id===sort.column)?.title||sort.column)+ " "+sort.direction).join(", ");this.sortState=sorts[0]||null;ROW_HELPERS.buildHeader.call(this);for(const cell of this.headCells){const column=this.columns.find(column=>column.id===cell.getAttribute("data-cx-column"));if(column?.className)cell.classList.add(...column.className.split(/ +/));
|
|
862
903
|
if(column?.alignment)cell.style.textAlign=column.alignment;const button=cell.querySelector(".cx-sort");if(fixedText)cell.title +="\nAlways sorted by "+fixedText+" first";if(button){const label=h("span","cx-htext");setText(label,button.textContent);button.replaceChildren(label)}
|
|
863
904
|
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));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)}
|
|
@@ -867,7 +908,7 @@ setClass(element,"cx-row" +(row.status?" cx-status cx-"+row.status:this.autoRows
|
|
|
867
908
|
}):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}
|
|
868
909
|
const column=this.cols[i],cell=row.h.cells[column.id];this.paintSlot(slot,row.h,column,this.columnIndex[i],row);if(!column.render&&!slot.text.data&&!(isCellObject(cell)&&cellValue(cell)==null&&cell.text==="")){slot.text.data="—";slot.cell.classList.add("p-missing");slot.missing=true}
|
|
869
910
|
else if(slot.missing){slot.cell.classList.remove("p-missing");slot.missing=false}
|
|
870
|
-
if(this.phone||slot.hiddenFields||slot.phonePrimary)this.paintHiddenFields?.(slot,row.h,column,this.columnIndex[i])}
|
|
911
|
+
if(this.phone||this.desktopHiddenFields||slot.hiddenFields||slot.phonePrimary)this.paintHiddenFields?.(slot,row.h,column,this.columnIndex[i])}
|
|
871
912
|
this.paintGridHighlights(element,row);if(row.status)this.paintGridSkeleton(element,row)}});class AxisWindow{constructor(){this.count=0;this.uniform=0;this.offsets=null;this.total=0}
|
|
872
913
|
reset(sizes,count){if(typeof sizes==="number"){this.count=count;this.uniform=sizes;this.offsets=null;this.total=count*sizes}else{const offsets=new Float64Array(sizes.length+1);for(let index=0;index<sizes.length;index++)offsets[index+1]=offsets[index]+ sizes[index];this.count=sizes.length;this.uniform=0;this.offsets=offsets;this.total=offsets[sizes.length]}
|
|
873
914
|
return this}
|
|
@@ -888,7 +929,7 @@ this.total=this.rowOffsets.top(count);this.syncScrollRange(this.logicalTop());th
|
|
|
888
929
|
if(changed){this.total=this.rowOffsets.top(this.rowOffsets.count);this.syncScrollRange(rows.rowTop(anchor.index)+ anchor.offset)}
|
|
889
930
|
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));
|
|
890
931
|
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()}
|
|
891
|
-
return measured};DataGrid.prototype.fitHeight=function(){if(this.view.autoHeight===false||this.pendingQuery||!this.grid.clientWidth||!this.host.checkVisibility())return;const grid=this.grid,style=this.host.style,box=grid.getBoundingClientRect(),computed=getComputedStyle(grid);const borders=parseFloat(computed.borderTopWidth)+ parseFloat(computed.borderBottomWidth);const scrollbar=Math.max(0,grid.offsetHeight-grid.clientHeight-borders);const content=[...grid.children].reduce((sum,child)=>sum+child.getBoundingClientRect().height,borders+scrollbar);if(this.fitInline===undefined){const own=getComputedStyle(this.host).maxHeight;this.fitInline=style.maxHeight;this.fitMax=own==="none"||!own?Infinity:/^-?[\d.]+px$/.test(own) ? parseFloat(own) : own;
|
|
932
|
+
return measured};DataGrid.prototype.fitHeight=function(){if(this.view.autoHeight===false||this.pendingQuery||!this.grid.clientWidth||!this.host.checkVisibility())return;const grid=this.grid,style=this.host.style,box=grid.getBoundingClientRect(),computed=getComputedStyle(grid);const borders=parseFloat(computed.borderTopWidth)+ parseFloat(computed.borderBottomWidth);const scrollbar=Math.max(0,grid.offsetHeight-grid.clientHeight-borders);const content=[...grid.children].reduce((sum,child)=>{const position=getComputedStyle(child).position;return position==="absolute"||position==="fixed"?sum:sum+child.getBoundingClientRect().height},borders+scrollbar);if(this.fitInline===undefined){const own=getComputedStyle(this.host).maxHeight;this.fitInline=style.maxHeight;this.fitMax=own==="none"||!own?Infinity:/^-?[\d.]+px$/.test(own) ? parseFloat(own) : own;
|
|
892
933
|
}
|
|
893
934
|
const hostStyle=getComputedStyle(this.host);const chrome=[...this.host.children].filter(child=>child!==grid).reduce((sum,child)=>{const css=getComputedStyle(child);return css.position==="absolute"||css.position==="fixed"?sum:sum+child.getBoundingClientRect().height},parseFloat(hostStyle.borderTopWidth)+ parseFloat(hostStyle.borderBottomWidth)+ parseFloat(hostStyle.paddingTop)+ parseFloat(hostStyle.paddingBottom));const height=Math.ceil(Math.max(this.host.getBoundingClientRect().height-box.height,chrome)+ content),bound=this.fitMax;const value=!this.rowCount()?this.fitInline:typeof bound==="number"?Math.min(bound,height)+ "px":"min("+height+"px, "+bound+")";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}
|
|
894
935
|
function gridRank(column){const value=column.priority;return typeof value==="number"?value:value==="optional"?10000:value==="essential"||value===undefined&&column.identity?0:5000}
|
|
@@ -927,19 +968,21 @@ if(total>width&&remaining>1){column.fitHidden=true;total -=size(column);remainin
|
|
|
927
968
|
if(identity&&!identity.fixed&&!identity.sized)total +=Math.min(identity.need??identity.width,(identity.indentNeed||0)+ 2*(this.fitCh||88))+(identity.reserve||0)- size(identity);if(identity)for(const column of shown.filter(column=>hide(column,true)).sort(order))
|
|
928
969
|
if(total>width){column.fitHidden=true;total -=size(column)}}};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"}
|
|
929
970
|
grid.columns=["left","none","right"].flatMap(side=>grid.columns.filter(column=>column.pin===side))}
|
|
930
|
-
Object.assign(DataGrid.prototype,COLUMN_FIT,{fitAvailableWidth(){const grid=this.grid;if(!grid.clientWidth)return 0;const style=this.fitContentWidth===undefined&&getComputedStyle(grid);const width=style?grid.clientWidth-parseFloat(style.paddingLeft)- parseFloat(style.paddingRight)- 1:this.fitContentWidth;return Math.max(0,Math.floor(width))},fitHide(shown,width,phone){COLUMN_FIT.fitHide.call(this,shown,width,phone
|
|
931
|
-
for(const column of peers)if(!column.fixed&&!column.sized&&!column.alwaysVisible)column.fitHidden=true},fitSize(column,level){if(column.fixed||column.sized)return columnWidth(column,column.width);const need=column.need??column.width,content=column.autoSize==="header"?need:column.contentNeed??need;const header=column.headWrapNeed||0;
|
|
932
|
-
return Math.ceil(columnWidth(column,width))},fitShrink(cols,excess){const slack=cols.map(column=>Math.max(0,column.renderWidth-this.fitSize(column,
|
|
971
|
+
Object.assign(DataGrid.prototype,COLUMN_FIT,{fitAvailableWidth(){const grid=this.grid;if(!grid.clientWidth)return 0;const style=this.fitContentWidth===undefined&&getComputedStyle(grid);const width=style?grid.clientWidth-parseFloat(style.paddingLeft)- parseFloat(style.paddingRight)- 1:this.fitContentWidth;return Math.max(0,Math.floor(width))},fitHide(shown,width,phone){COLUMN_FIT.fitHide.call(this,shown,width,phone);if(!phone||(this.layoutMode||"fit")!=="fit")return;const identity=shown.find(column=>column.identity);if(!identity)return;for(const column of shown)if(!column.identity&&gridLevel(column)>0&&!column.alwaysVisible&&!column.fixed&&!column.sized)column.fitHidden=true;const peers=shown.filter(column=>!column.fitHidden&&!column.identity&&gridLevel(column)===0);if(identity.fixed||identity.sized)return;const primary=columnWidth(identity,Math.min(Math.max(identity.contentNeed||0,identity.pref||0,identity.headWrapNeed||0),Math.max(160,width*.65)));if(primary+peers.reduce((sum,column)=>sum+this.fitSize(column,2),0)>width)
|
|
972
|
+
for(const column of peers)if(!column.fixed&&!column.sized&&!column.alwaysVisible)column.fitHidden=true},fitSize(column,level){if(column.fixed||column.sized)return columnWidth(column,column.width);const need=column.need??column.width,content=column.autoSize==="header"?need:column.contentNeed??need;const header=column.headWrapNeed||0;let width=Math.max(level===undefined?need:content,header,level===undefined?column.pref||0:0);if(level===0&&this.desktopHiddenFields&&gridLevel(column)&&gridTextual(column))width=Math.max(column.minW,width*.8);if(column===this.desktopDisclosureColumn)width +=this.desktopDisclosureWidth;if(column.identity&&this.host.clientWidth<=640&&(this.layoutMode||"fit")==="fit"){const available=this.fitAvailableWidth(),minimum=Math.min(content,available*.4);width=Math.max(header,minimum,Math.min(width,Math.max(minimum,available*.5)))}
|
|
973
|
+
return Math.ceil(columnWidth(column,width))},fitShrink(cols,excess,level){const slack=cols.map(column=>Math.max(0,column.renderWidth-this.fitSize(column,level)));const total=slack.reduce((sum,width)=>sum+width,0),ratio=total?Math.min(1,excess / total) : 0;
|
|
933
974
|
cols.forEach((column,index)=>column.renderWidth -=slack[index]*ratio);return Math.max(0,excess-total)},fitSpare(columns,spare){const flex=columns.filter(column=>column.flexWeight);const first=gridFitGrow(flex,spare,column=>column.flexWeight);return gridFitGrow(columns.filter(gridTextual),first.spare,column=>column.contentNeed||column.need).target||first.target},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}
|
|
934
975
|
this.headCells.forEach((cell,index)=>{cell.querySelector(".cx-sort,.cx-htext").title=cell.title;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})}
|
|
935
976
|
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();if(this.phone!==undefined)this.updateGridPhone();const visible=this.visibleColumns(),cols=this.cols,width=this.fitAvailableWidth();if(cols&&(visible.length!==cols.length||visible.some((column,index)=>column!==cols[index]))){if(!this.fitBuilding){this.buildHeader();this.syncColumnControls();this.render();return}
|
|
936
977
|
queueMicrotask(()=>this.destroyed||this.applyWidths())}
|
|
937
|
-
FIT.applyWidths.call(this,this.fitWidths(cols,width));if(this.headRow.matches(":hover"))this.syncColumnMenuRoom?.();this.fitHeight()},fitLayout(){const width=this.fitAvailableWidth(),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)column.need=this.measureGridColumn(column).width}
|
|
938
|
-
for(const column of this.columns){if(!column.fixed&&!column.sized)column.width=this.fitSize(column)}
|
|
939
|
-
this.fitHide(shown,width,phone)},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(head?.paddingRight)||12];this.fitFont.push(gridClassFont(this.grid,"p-mono"),gridClassFont(this.grid,"p-sub"),gridClassFont(this.grid,"p-pill",this.fitPill={}),gridClassFont(this.grid,"cx-sort-priority"));this.fitNumericVariant=gridNumericVariant(this.grid);context.font=this.fitFont[0];this.fitCh=Math.ceil(gridMeasureText(context,"00000000000",this.fitNumericVariant))}
|
|
978
|
+
FIT.applyWidths.call(this,this.fitWidths(cols,width));if(this.headRow.matches(":hover"))this.syncColumnMenuRoom?.();this.fitHeight()},fitLayout(){const width=this.fitAvailableWidth(),phone=this.host.clientWidth<=640,shown=FIT.visibleColumns.call(this).filter(column=>!column.responsiveHidden);if(!width)return;const compact=!phone&&shown.some(c=>gridLevel(c)>1);this.host.classList.toggle("cx-narrow",compact);const key=this.pendingQuery?this.fitKey:this.queryEpoch+":"+phone+compact;if(key!==this.fitKey||!this.fitCh){this.fitKey=key;this.fitFont=null;for(const column of this.columns)column.need=this.measureGridColumn(column).width}
|
|
979
|
+
this.desktopDisclosureColumn=null;for(const column of this.columns){if(!column.fixed&&!column.sized)column.width=this.fitSize(column)}
|
|
980
|
+
this.fitHide(shown,width,phone);const hidden=this.columns.filter(column=>!column.hidden&&(column.fitHidden||column.responsiveHidden));this.desktopHiddenFields=!phone&&hidden.length>0;if(this.desktopHiddenFields){this.desktopDisclosureColumn=shown.find(column=>!column.fitHidden);this.fitContext.font=this.fitFont[0];const label=fill(plural(this.labels.hiddenColumns,hidden.length,this.lang),{count:numberFormat(this.lang).format(hidden.length)});this.desktopDisclosureWidth=Math.ceil(this.fitContext.measureText(label).width)+ 12}},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(head?.paddingRight)||12];this.fitFont.push(gridClassFont(this.grid,"p-mono"),gridClassFont(this.grid,"p-sub"),gridClassFont(this.grid,"p-pill",this.fitPill={}),gridClassFont(this.grid,"cx-sort-priority"));this.fitNumericVariant=gridNumericVariant(this.grid);context.font=this.fitFont[0];this.fitCh=Math.ceil(gridMeasureText(context,"00000000000",this.fitNumericVariant))}
|
|
940
981
|
const[,,headFont,pad,mono,sub,pillFont,priorityFont]=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 move=this.view.reporting?.columnReorder?Math.max(32,Math.ceil(text("⋮⋮",headFont,false))+ 8):0,target=this.host.clientWidth<=640?44:24;const sort=query?.sort.findIndex(sort=>sort.column===column.id)??-1;const chrome=2*pad+8+move +(sort<0?0:22 +(query.sort.length>1?text(sort+1,priorityFont,false)+ 4:0))+
|
|
941
982
|
(gridColumnFiltered(query,column.id)?target:0)+(first&&this.selectionControls?target+2:0);const title=text(column.title,headFont,false),head=Math.ceil(Math.max(24,title)+ chrome);const words=column.title.trim().split(/\s+/);
|
|
942
|
-
let balanced=title;for(let split=1;split<words.length;split++)balanced=Math.min(balanced,Math.max(text(words.slice(0,split).join(" "),headFont,false),text(words.slice(split).join(" "),headFont,false)));column.headWrapNeed=Math.ceil(Math.max(24,balanced)+ chrome);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
|
|
983
|
+
let balanced=title;for(let split=1;split<words.length;split++)balanced=Math.min(balanced,Math.max(text(words.slice(0,split).join(" "),headFont,false),text(words.slice(split).join(" "),headFont,false)));column.headWrapNeed=Math.ceil(Math.max(24,balanced)+ chrome);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 change=options.beforeAfter&&isCellObject(cell)&&cell.changes?.[0];if(change){const spec={...column,kind:change.kind||column.kind};values=Math.max(values,Math.min(420,text(changeCellText(change.before,spec,this.fmt))+ text("→")+
|
|
984
|
+
text(changeCellText(change.after,spec,this.fmt))+ 12));sampled++;continue}
|
|
985
|
+
const tone=columnValueTone(column,cell)||gridPresenterTone(options,cellValue(cell),isCellObject(cell)&&cell.tone),quiet=options.quietTones?.includes(tone);const secondary=kind==="identifier"&&(options.secondaryColumn?cellText(row.h.cells[options.secondaryColumn],this.columns.find(item=>item.id===options.secondaryColumn),this.fmt):isCellObject(cell)&&cell.secondary);const atomic=kind==="enum"||kind==="state";const pill=kind==="enum"&&!quiet;const atomicWidth=atomic?text(value,pill?pillFont:undefined)+
|
|
943
986
|
(pill?this.fitPill.extra+this.fitPill.spacing*Array.from(value).length:quiet&&kind==="enum"?16:quiet?0:kind==="state"&&tone!=="success"?15:13):0;values=Math.max(values,atomic?atomicWidth:kind==="identifier"?Math.min(420,text(value,options.mono?mono:undefined,!options.mono)+(secondary?6+text(secondary,sub):0)):kind==="statusStrip"?Math.min(series,12)*7:kind==="sparklineBars"?Math.min(series,12)*6:kind==="sparklineLine"?80:kind==="boolean"?16:
|
|
944
987
|
Math.min(kind==="longText"||kind==="tags"||kind==="list"||column.wrap?320:420,text(value))+
|
|
945
988
|
(kind==="progress"?80:kind==="tags"||kind==="list"?48:0));sampled++}
|
|
@@ -952,9 +995,9 @@ DataGrid.prototype.paintGridHighlights=function(element,row){if(!this.view.highl
|
|
|
952
995
|
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",toneVariable(name,false,"var(--hfx-color-text,light-dark(#16181c,#f2f3f5))"));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;
|
|
953
996
|
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}};const VALUE_PAINT=DataGrid.prototype.paintSlot;DataGrid.prototype.paintSlot=function(slot,row,column,index,wrapper){VALUE_PAINT.call(this,slot,row,column,index,wrapper);if(!column.render){const cell=row.cells[column.id],rule=columnNumericTone(column,cellValue(cell)),tone=columnValueTone(column,cell);if(tone){applyTone(slot.cell,slot.base,toneOf(tone));slot.cls=slot.cell.className}
|
|
954
997
|
if(rule){slot.cell.title=rule.label;slot.title=rule.label}
|
|
955
|
-
setAttr(slot.cell,"aria-description",rule?.label||null)}};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(
|
|
956
|
-
["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()}
|
|
957
|
-
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||
|
|
998
|
+
setAttr(slot.cell,"aria-description",rule?.label||null)}};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((options.kind==="progress"||options.kind==="change"&&!options.beforeAfter)&&column.kind!=="number"||
|
|
999
|
+
["boolean","list","tags","link"].includes(options.kind)&&column.kind!=="text")invalid();for(const name of["newWindow","inverted","mono","higherIsGood","beforeAfter"])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()}
|
|
1000
|
+
if(options.kind==="change"&&!options.beforeAfter&&!options.deltaColumn||options.beforeAfter&&(options.kind!=="change"||options.deltaColumn!==undefined)||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||
|
|
958
1001
|
options.limit!==undefined&&(!Number.isInteger(options.limit)||options.limit<1||options.limit>16)))invalid();if((options.minimum===undefined)!==(options.scaleMaximum===undefined)||options.minimum!==undefined&&
|
|
959
1002
|
(![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)||
|
|
960
1003
|
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();
|
|
@@ -969,11 +1012,11 @@ const value=raw.trim();try{const url=new URL(value,document.baseURI);if(["http:"
|
|
|
969
1012
|
}catch(_){/* Invalid destinations retain plain text. */ }
|
|
970
1013
|
return null}
|
|
971
1014
|
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}}
|
|
972
|
-
return GRID_PRESENTERS_BUILD.call(this)};const GRID_PRESENTER_IMPL={},GRID_PRESENTERS=definePart(DataGrid,"presenters","cell presenters",()=>({GRID_PRESENTER_IMPL,h,setText,fill,cellValue,cellText,isCellObject,columnNumericTone,columnValueTone,gridPresenterTone,gridPresenterLink,numberFormat,toneColors,toneOf,applyTone,gridIcon,badgeElement,GRID_HISTORY_KINDS,GRID_HISTORY_IMPL,RECORD_IMPL}));function gridCellRenderer(grid,column,options,record=false,complete=false){let paint;return(row,element)=>{if(!GRID_PRESENTER_IMPL.create)return false;return(paint||=GRID_PRESENTER_IMPL.create(grid,column,options,record,complete))(row,element)}}
|
|
1015
|
+
return GRID_PRESENTERS_BUILD.call(this)};const GRID_PRESENTER_IMPL={},GRID_PRESENTERS=definePart(DataGrid,"presenters","cell presenters",()=>({GRID_PRESENTER_IMPL,h,setText,fill,cellValue,cellText,changeCellText,isCellObject,columnNumericTone,columnValueTone,gridPresenterTone,gridPresenterLink,numberFormat,toneColors,toneOf,applyTone,gridIcon,badgeElement,GRID_HISTORY_KINDS,GRID_HISTORY_IMPL,RECORD_IMPL}));function gridCellRenderer(grid,column,options,record=false,complete=false){let paint;return(row,element)=>{if(!GRID_PRESENTER_IMPL.create)return false;return(paint||=GRID_PRESENTER_IMPL.create(grid,column,options,record,complete))(row,element)}}
|
|
973
1016
|
DataGrid.prototype.loadPresenters=function(){return this.presenterLoad||=loadPart(GRID_PRESENTERS).then(()=>{this.version++;this.scheduleRender()})};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})}
|
|
974
1017
|
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}
|
|
975
1018
|
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}
|
|
976
|
-
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}
|
|
1019
|
+
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,tupleFilters: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}
|
|
977
1020
|
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}
|
|
978
1021
|
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)}
|
|
979
1022
|
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;if(focus)this.focusOrigin=null;this.focused=row;this.revealGridRow(row,row._ri);if(this.render()){this.revealGridRow(row,row._ri);this.render()}
|
|
@@ -1003,16 +1046,18 @@ if(event.key!=="F2")return false;GRID_DRAG.picked=gridDragItem(this,row);setText
|
|
|
1003
1046
|
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}
|
|
1004
1047
|
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. */ }
|
|
1005
1048
|
}}
|
|
1006
|
-
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.view.rowIdentity==="ordinal"?[]:[...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)||
|
|
1049
|
+
try{this.initialState=stored&&stored.current?this.readGridState(stored.current):null}catch(_){this.initialState=null}},initializeState(){const start=()=>{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};return gridUsesCalculations(this.view)&&!this.calculationPrepared?this.prepareCalculations().then(start):start()},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.view.rowIdentity==="ordinal"?[]:[...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)||
|
|
1007
1050
|
!Number.isFinite(state.scrollTop)||state.scrollTop<0||state.scrollTop>MAX_PROTOCOL_INTEGER*this.rowH||!Array.isArray(state.selection))
|
|
1008
1051
|
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.rowIdentity==="ordinal"&&selection.length)
|
|
1009
1052
|
throw new Error("CanopyX: ordinal row identity is snapshot-local; replaying saved selections requires stable row keys.");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")||
|
|
1010
1053
|
!Number.isInteger(paging.offset)||paging.offset<0||paging.offset>MAX_PROTOCOL_INTEGER||this.cursor&&paging.offset!==0)
|
|
1011
1054
|
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)}
|
|
1012
|
-
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);if(this.view.rowIdentity!=="ordinal")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||
|
|
1055
|
+
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 restore={state,epoch:this.queryEpoch+1};this.stateQueryRestore=restore;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.stateQueryRestore===restore)this.stateQueryRestore=null;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);if(this.view.rowIdentity!=="ordinal")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||
|
|
1013
1056
|
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}))}))}
|
|
1014
1057
|
catch(_){/* A disabled or full storage leaves the current session usable. */ }
|
|
1015
|
-
}});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
|
|
1058
|
+
}});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_BOARD=definePart(DataGrid,"board","board view",()=>({DataGrid,h,setText,fill,cellValue,cellText,gridScalar,gridQueryRows,gridQuery,validateGridPage,validateGridCursorPage,numberFormat,PROTOCOL}));const GRID_BOARD_INIT=DataGrid.prototype.initializeState;DataGrid.prototype.initializeState=function(){const start=()=>GRID_BOARD_INIT.call(this);return this.view.board?loadPart(GRID_BOARD).then(()=>{if(!this.destroyed)this.startBoard()}).then(start):start()};DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridBucket:"Bucket",gridBucketNone:"Exact values",gridBucketDay:"Day",gridBucketWeek:"Week",gridBucketMonth:"Month",gridBucketQuarter:"Quarter",gridBucketYear:"Year",gridBucketHour:"Hour of day",gridBucketWeekday:"Weekday",gridBucketWidth:"Range width",gridBucketEdges:"Range edges",gridBucketOrigin:"Range origin",gridBucketInvalid:"Enter valid bucket settings."}));const BUCKETS_PART=definePart(DataGrid,"buckets","date and range buckets",()=>({GRID_BUCKETS}));const gridUsesBuckets=view=>view.reporting?.groupBy?.some(level=>level.bucket)||view.facets?.columns?.some(facet=>facet?.bucket)||
|
|
1059
|
+
view.query?.groupBy?.some(level=>level.bucket)||gridQueryUsesBucketFilters(view.query);function gridQueryUsesBucketFilters(query){const group=node=>!!node&&(node.filters?.some(filter=>filter.bucket)||node.groups?.some(group));return query?.filters?.some(filter=>filter.bucket)||group(query?.filterGroup)||query?.tupleFilters?.some(tuple=>tuple.columns?.some(field=>field.bucket))}
|
|
1060
|
+
const BUCKET_REQUEST_PAGE=DataGrid.prototype.requestPage;DataGrid.prototype.requestPage=async function(query,signal){if(gridQueryUsesBucketFilters(query))await loadPart(BUCKETS_PART);return BUCKET_REQUEST_PAGE.call(this,query,signal)};DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridCalculationAdd:"Add calculated column",gridCalculationTitle:"Column title",gridCalculationId:"Column ID",gridCalculationExpression:"Expression",gridCalculationError:"Calculation error: {error}",gridCalculationInvalid:"Check the expression, column references and result kind.",gridCalculationHelp:"Use [columnId], arithmetic, IF, COALESCE, ROUND, CONCAT, DATEPART or DATEDIFF.",gridCalculationResultType:"Result type",gridCalculationDivideZero:"Division by zero",gridCalculationNumber:"Number",gridCalculationDate:"Date and time"}));const GRID_CALCULATIONS={},CALCULATIONS_PART=definePart(DataGrid,"calculations","calculated columns",()=>({DataGrid,GRID_CALCULATIONS,requireIdentifier,cellValue,h,setText,fill,gridButton,gridQuery,gridQueryStart,inlineGridSource,rowDataSource,loadGridFilterEditor,gridFreeze,gridFitPrepare}));const gridUsesCalculations=view=>view.calculatedColumnsEditable||view.columns.some(column=>column.calculation!==undefined);Object.assign(DataGrid.prototype,{async prepareCalculations(){if(!gridUsesCalculations(this.view))return;await loadPart(CALCULATIONS_PART);GRID_CALCULATIONS.prepare(this)},async setCalculatedColumns(columns){await loadPart(CALCULATIONS_PART);return this.setCalculatedColumns(columns)},getCalculatedColumns(){return GRID_CALCULATIONS.definitions?.(this.view)||[]},async openCalculatedColumn(anchor){await loadPart(CALCULATIONS_PART);return this.openCalculatedColumn(anchor)}});const GRID_CALENDAR=definePart(DataGrid,"calendar","calendar",()=>({DataGrid,h,setText,fill,plural,gridButton,gridIcon,gridFreeze,cellText,cellValue,isCellObject,gridQuery,gridQueryRows,gridResultShape,gridPresenterTone,validateGridRows,repairGridCell,validateGridPage,validateGridCursorPage,validStructuralCount,checkDocument,numberFormat,loadPart,HostCellContext,disposeHostSlot,PROTOCOL,toneVariable,bindContextMenu}));definePart(DataGrid,"timeline","resource timeline",()=>({DataGrid,TreeGrid,h,setText,fill,numberFormat,toneVariable}));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","editCalendarOccurrence","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","resourceIds","recurrence","exceptions","series","recurrenceId","source"];const CAL_TITLES={allDay:"All day",description:"Description",tone:"Tone",resource:"Resource",resourceIds:"Resources",series:"Series",recurrenceId:"Original start",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()))
|
|
1016
1061
|
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";
|
|
1017
1062
|
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;
|
|
1018
1063
|
const parsed=typeof value==="string"&&/^\d{4}-\d\d-\d\dT\d\d:\d\d/.test(value) ? new Date(value) : null;
|
|
@@ -1022,80 +1067,106 @@ for(const field of CAL_FIELDS)if(field!=="allDay"&&event[field]!=null){cells[fie
|
|
|
1022
1067
|
return{id,cells}});if(options.resources)used.add("resource");if(used.has("recurrence"))for(const field of["series","recurrenceId","exceptions"])used.add(field);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:field==="recurrenceId"?"datetime":"text",hidden:field!=="description",editable:writable&&(field==="resource"||field==="resourceIds"||field==="series"||field==="recurrenceId"||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})}
|
|
1023
1068
|
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)}
|
|
1024
1069
|
return roots}
|
|
1025
|
-
const GRID_EDITING=definePart(DataGrid,"editing","editing",()=>({DataGrid,FORMS_IMPL,h,setText,fill,gridButton,gridDecorateButton,GRID_ICONS,cellValue,cellText,isCellObject,gridFreeze,gridMomentFromParts,numberFormat,gridQuery,foldText,requireIdentifier,checkDocument,validateGridRows,inlineGridSource,PROTOCOL,boundedPattern,validateGridFacetPage,RECORD_IMPL}));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:"
|
|
1070
|
+
const GRID_EDITING=definePart(DataGrid,"editing","editing",()=>({DataGrid,FORMS_IMPL,h,setText,fill,gridButton,gridDecorateButton,GRID_ICONS,cellValue,cellText,isCellObject,gridFreeze,gridMomentFromParts,numberFormat,gridQuery,foldText,requireIdentifier,checkDocument,validateGridRows,inlineGridSource,PROTOCOL,boundedPattern,validateGridFacetPage,RECORD_IMPL}));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:"Refine",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,gridKeyCompare,gridFacetKey,cellText,foldText,numberFormat,gridCellRenderer,gridFreeze,overlayTheme,watchOverlayTheme,GRID_BUCKETS,gridBucketDefinition}));Object.assign(DataGrid.prototype,{loadFacets(){return this.facetLoad||=Promise.resolve().then(async()=>{if((this.opts.facets?.columns||this.view.facets?.columns)?.some(facet=>facet?.bucket))await loadPart(BUCKETS_PART);await loadPart(GRID_FACETS);if(!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")
|
|
1026
1071
|
throw new Error("CanopyX: invalid saved facet panel state.");if(this.view.facets||this.opts.facets)state.facets={open:facets.open}}
|
|
1027
1072
|
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}
|
|
1028
|
-
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,toneVariable,numberFormat,gridButton,gridOption,gridInputLabel,gridIcon,gridDecorateButton,GRID_REPORTING,loadPart,definePart,announceMenu,boundedPattern,wildcardMatch,svgIcon,overlayTheme}));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,gridIdOrder,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(){if(this.view.rowIdentity==="ordinal")return Promise.reject(new Error("CanopyX: ordinal row identity is snapshot-local; keyed live updates require stable row keys."));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_OPTIONS_PART=definePart(DataGrid,"grid-options","grid footer and query extensions",()=>({DataGrid,GRID_OPTIONS,GRID_OPTION_GRIDS,h,setText,fill,gridQuery,gridQueryRows,gridCompare,gridKeyCompare,gridSortScalar,gridMatches,gridMatchesGroup,gridResultShape,GRID_REPORTING,gridScalar,gridFreeze,gridOperationActive,numberText,isCellObject,cellValue,requireIdentifier,validStructuralCount,PROTOCOL}));const OPTIONS_REQUEST=DataGrid.prototype.requestPage;DataGrid.prototype.requestPage=async function(query,signal){if(!gridOptionsUsed(this.view,this.plugins))return OPTIONS_REQUEST.call(this,query,signal);await loadPart(GRID_OPTIONS_PART);gridOperationActive(this,signal);GRID_OPTIONS.attach(this);const page=await OPTIONS_REQUEST.call(this,query,signal);return GRID_OPTIONS.page(this,query,signal,page)};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())
|
|
1029
|
-
.catch(error=>{this.pager.hidden=this.opts.toolbar===false;this.emit("error",{error})})]).then(([result])=>result)};const
|
|
1030
|
-
|
|
1073
|
+
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,toneVariable,numberFormat,gridButton,gridVariant,gridOption,gridInputLabel,gridIcon,gridDecorateButton,GRID_REPORTING,loadPart,definePart,announceMenu,boundedPattern,wildcardMatch,svgIcon,overlayTheme}));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,gridIdOrder,validateGridRows,validateGridPage,validateGridCursorPage,gridFreeze,requireIdentifier,checkDocument,cellValue,PROTOCOL,GRID_REPORTING,GRID_CALCULATIONS,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(){if(this.view.rowIdentity==="ordinal")return Promise.reject(new Error("CanopyX: ordinal row identity is snapshot-local; keyed live updates require stable row keys."));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_OPTIONS_PART=definePart(DataGrid,"grid-options","grid footer and query extensions",()=>({DataGrid,GRID_OPTIONS,GRID_OPTION_GRIDS,h,setText,fill,gridQuery,gridQueryRows,gridCompare,gridKeyCompare,gridSortScalar,gridMatches,gridMatchesGroup,gridResultShape,GRID_REPORTING,gridScalar,gridFreeze,gridOperationActive,numberText,isCellObject,cellValue,requireIdentifier,validStructuralCount,PROTOCOL}));const OPTIONS_REQUEST=DataGrid.prototype.requestPage;DataGrid.prototype.requestPage=async function(query,signal){if(!gridOptionsUsed(this.view,this.plugins))return OPTIONS_REQUEST.call(this,query,signal);await loadPart(GRID_OPTIONS_PART);gridOperationActive(this,signal);GRID_OPTIONS.attach(this);const page=await OPTIONS_REQUEST.call(this,query,signal);return GRID_OPTIONS.page(this,query,signal,page)};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())
|
|
1074
|
+
.catch(error=>{this.pager.hidden=this.opts.toolbar===false;this.emit("error",{error})})]).then(([result])=>result)};const GRID_PATH=definePart(DataGrid,"path","path view",()=>({DataGrid,h,setText,fill,cellValue,cellText,badgeElement,applyTone,toneOf,PROTOCOL}));const GRID_PATH_INIT=DataGrid.prototype.initializeState;DataGrid.prototype.initializeState=function(){const start=()=>this.destroyed?undefined:GRID_PATH_INIT.call(this);return this.view.path?loadPart(GRID_PATH).then(()=>{if(!this.destroyed)this.validatePathSource()}).then(start).then(async result=>{if(!this.destroyed)await this.startPath();return result}):start()};const GRID_PLAN=definePart(DataGrid,"plan","plan view",()=>({DataGrid,h,setText,fill,cellValue,cellText,gridFreeze}));const GRID_PLAN_INIT=DataGrid.prototype.initializeState;DataGrid.prototype.initializeState=function(){const start=()=>GRID_PLAN_INIT.call(this);return this.view.plan?loadPart(GRID_PLAN).then(async()=>{this.validatePlanDefinition();const result=await start();if(this.view.rowData&&!this.opts.source){await this.source.decoded;this.validatePlanDefinition(true)}
|
|
1075
|
+
return result}):start()};const PRESENTERS_INIT=DataGrid.prototype.initializeState;DataGrid.prototype.initializeState=function(){const ready=PRESENTERS_INIT.call(this);return[...this.columns,...(this.view.record?.fields||[])].some(column=>column.presenter||column.wrap)?Promise.all([ready,this.loadPresenters()]).then(([result])=>result):ready};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")||
|
|
1076
|
+
!!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,repairGridCell,inlineGridSource,loadSidecar,gridIdOrder,gridInlineRevision}));function rowDataSource(grid){let inner=null;const source={reset:()=>{inner=null},query:(query,options)=>
|
|
1031
1077
|
(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}
|
|
1032
|
-
class MatrixHost{}
|
|
1033
|
-
const MATRIX_IMPL={},activeMatrices=new WeakMap();const MATRIX_PART=definePart(MatrixHost,"matrix","matrix view",()=>({MATRIX_IMPL,h,setText,fill,plural,foldText,numberFormat,gridFreeze,toneOf,applyTone,toneColors,AxisWindow,PROTOCOL}));const MATRIX_METHODS=["sort","group","filter","setSearch","setAttentionOnly","getState","setState","getVisible","focusCell","update"];function createMatrix(host,options){if(!host||!options||!options.view||typeof options.view!=="object")throw new Error("CanopyX: createMatrix needs a host element and a matrix document.");const existing=activeMatrices.get(host);if(existing)return existing;const addedClasses=["cx-host","cx-matrix-host"].filter(name=>!host.classList.contains(name)),hadClass=host.hasAttribute("class");const previousError=host.getAttribute("data-cx-error");let stopTheme;let impl=null,destroyed=false,notice=null,errorText=null;const cancelled=()=>new DOMException("Matrix was destroyed.","AbortError");const
|
|
1034
|
-
|
|
1035
|
-
|
|
1078
|
+
const GRID_STACK=definePart(DataGrid,"stack","stack view",()=>({DataGrid,h,setText,fill,cellValue,cellText,badgeElement,applyTone,toneOf,PROTOCOL}));const GRID_STACK_INIT=DataGrid.prototype.initializeState;DataGrid.prototype.initializeState=function(){const start=()=>this.destroyed?undefined:GRID_STACK_INIT.call(this);return this.view.stack?loadPart(GRID_STACK).then(()=>{if(!this.destroyed)this.validateStackSource()}).then(start).then(async result=>{if(this.destroyed)return result;if(this.view.rowData)await this.source.decoded;if(!this.destroyed)this.startStack();return result}):start()};class MatrixHost{}
|
|
1079
|
+
const MATRIX_IMPL={},activeMatrices=new WeakMap();const MATRIX_PART=definePart(MatrixHost,"matrix","matrix view",()=>({MATRIX_IMPL,h,setText,fill,plural,foldText,numberFormat,gridFreeze,toneOf,applyTone,toneColors,AxisWindow,PROTOCOL}));const MATRIX_METHODS=["sort","group","filter","setSearch","setAttentionOnly","getState","setState","getVisible","focusCell","update"];function createMatrix(host,options){if(!host||!options||!options.view||typeof options.view!=="object")throw new Error("CanopyX: createMatrix needs a host element and a matrix document.");const existing=activeMatrices.get(host);if(existing)return existing;const addedClasses=["cx-host","cx-matrix-host"].filter(name=>!host.classList.contains(name)),hadClass=host.hasAttribute("class");const previousError=host.getAttribute("data-cx-error");let stopTheme;let impl=null,destroyed=false,notice=null,errorText=null,failure=null,loaded=false;const cancelled=()=>new DOMException("Matrix was destroyed.","AbortError");const clearError=()=>{notice?.remove();notice=null;if(errorText!==null&&host.getAttribute("data-cx-error")===errorText){if(previousError===null)host.removeAttribute("data-cx-error");else host.setAttribute("data-cx-error",previousError)}
|
|
1080
|
+
errorText=null};const showError=error=>{if(destroyed)return;impl?.destroy();impl=null;failure=error;clearError();notice=h("div","cx-mount-error");notice.setAttribute("role","alert");setText(notice,error&&error.message?error.message:String(error));host.appendChild(notice);errorText=notice.textContent;host.setAttribute("data-cx-error",errorText);host.dispatchEvent(new CustomEvent("canopyx:error",{detail:{error},bubbles:true}))};const ready=loadPart(MATRIX_PART).then(()=>{if(destroyed)throw cancelled();loaded=true;impl=MATRIX_IMPL.create(host,options);return impl.ready});const api={element:host};Object.defineProperty(api,"view",{enumerable:true,get:()=>impl?impl.document():options.view});for(const name of MATRIX_METHODS)api[name]=(...args)=>{const invoke=()=>{if(destroyed)throw cancelled();if(!impl&&name==="update"){clearError();try{impl=MATRIX_IMPL.create(host,{...options,view:args[0]});failure=null;return impl.ready.then(()=>undefined)}
|
|
1081
|
+
catch(error){showError(error);throw error}}
|
|
1082
|
+
if(failure)throw failure;return impl[name](...args)};if(destroyed)throw cancelled();return loaded?invoke():ready.then(invoke)};api.ready=ready.then(()=>{if(destroyed)throw cancelled();return api});api.ready.catch(()=>{});api.destroy=()=>{if(destroyed)return;destroyed=true;impl?.destroy();stopTheme?.();if(activeMatrices.get(host)===api)activeMatrices.delete(host);if(host.canopyx===api)delete host.canopyx;host.removeAttribute("data-canopyx-mounted");clearError();for(const name of addedClasses)host.classList.remove(name);if(!hadClass&&!host.classList.length)host.removeAttribute("class")};ready.catch(showError);bindTimeSlider(api,options,"matrix",value=>MATRIX_IMPL.validate(value),showError);Object.freeze(api);activeMatrices.set(host,api);host.classList.add("cx-host","cx-matrix-host");stopTheme=watchTheme(host);host.setAttribute("data-canopyx-mounted","true");host.canopyx=api;return api}
|
|
1083
|
+
const MATRIX_API={createMatrix};class TimeSliderHost{}
|
|
1084
|
+
const TIME_SLIDER_IMPL={};const TIME_SLIDER_PART=definePart(TimeSliderHost,"time-slider","snapshot playback",()=>({TIME_SLIDER_IMPL,h,setText,fill,PROTOCOL}));function bindTimeSlider(api,options,kind,validate,reportError){const ownerReady=api.ready,update=api.update,destroy=api.destroy;let controller=null,revision=0,disposed=false;const cancelled=()=>new DOMException("Snapshot playback was replaced or destroyed.","AbortError");const install=async(view,token)=>{await ownerReady;if(disposed||token!==revision)throw cancelled();if(!Object.hasOwn(view,"timeSlider"))return;try{await loadPart(TIME_SLIDER_PART);if(disposed||token!==revision)throw cancelled();validate(view);controller=TIME_SLIDER_IMPL.create(api.element,view,options.labels,kind,validate,update)}catch(error){if(disposed||token!==revision)throw cancelled();if(error?.name!=="AbortError")reportError(error);throw error}};api.ready=ownerReady.then(async()=>{if(!revision&&!disposed)await install(options.view,0);return api});api.ready.catch(()=>{});api.update=view=>{const token=++revision;controller?.destroy();controller=null;if(disposed)throw cancelled();if(!view||!Object.hasOwn(view,"timeSlider"))return update(view);return install(view,token)};const call=name=>(...args)=>{if(disposed)throw cancelled();if(!controller)throw new Error("CanopyX: snapshot playback is not ready or this view has no time slider.");return controller[name](...args)};api.playback=Object.freeze(Object.fromEntries(["play","pause","step","seek","getState"].map(name=>[name,call(name)])));api.destroy=()=>{if(disposed)return;disposed=true;++revision;controller?.destroy();controller=null;destroy()}}
|
|
1085
|
+
class ChangesHost{}
|
|
1036
1086
|
const CHANGES_IMPL={},activeChangeViews=new WeakMap();const CHANGES_PART=definePart(ChangesHost,"changes","change views",()=>({CHANGES_IMPL,h,setText,fill,plural,numberFormat,gridFreeze,toneOf,applyTone,svgIcon,PROTOCOL}));const CHANGE_VIEW_METHODS={diff:["getLayout","setLayout","next","previous","expandAll","copy","getHunks","update"],changes:["getState","setState","toggle","focusItem","update"],propagation:["update"]};function changeViewFactory(kind,name){return function(host,options){if(!host||!options||!options.view||typeof options.view!=="object")throw new Error("CanopyX: "+name+" needs a host element and a "+kind+" document.");const existing=activeChangeViews.get(host);if(existing)return existing;const addedClasses=["cx-host","cx-change-host"].filter(className=>!host.classList.contains(className)),hadClass=host.hasAttribute("class"),previousError=host.getAttribute("data-cx-error");let stopTheme;let impl=null,destroyed=false,notice=null,errorText=null,failure=null,loaded=false,currentView=options.view;const cancelled=()=>new DOMException("Change view was destroyed.","AbortError");const clearError=()=>{notice?.remove();notice=null;if(errorText!==null&&host.getAttribute("data-cx-error")===errorText){if(previousError===null)host.removeAttribute("data-cx-error");else host.setAttribute("data-cx-error",previousError)}
|
|
1037
1087
|
errorText=null};const showError=error=>{if(destroyed)return;impl?.destroy();impl=null;failure=error;clearError();notice=h("div","cx-mount-error");notice.setAttribute("role","alert");setText(notice,error&&error.message?error.message:String(error));host.appendChild(notice);errorText=notice.textContent;host.setAttribute("data-cx-error",errorText);host.dispatchEvent(new CustomEvent("canopyx:error",{detail:{error},bubbles:true}))};const mount=view=>{clearError();impl=CHANGES_IMPL[kind](host,{...options,view});failure=null;return impl.ready};const ready=loadPart(CHANGES_PART).then(()=>{if(destroyed)throw cancelled();loaded=true;return mount(currentView)});const api={element:host};Object.defineProperty(api,"view",{enumerable:true,get:()=>impl?impl.document():currentView});for(const method of CHANGE_VIEW_METHODS[kind])api[method]=(...args)=>{const invoke=()=>{if(destroyed)throw cancelled();if(method==="update"){currentView=args[0];try{return impl?impl.update(...args):mount(currentView)}
|
|
1038
1088
|
catch(error){showError(error);throw error}}
|
|
1039
|
-
if(failure)throw failure;return impl[method](...args)};if(destroyed)throw cancelled();return loaded?invoke():ready.then(invoke)};api.ready=ready.then(()=>{if(destroyed)throw cancelled();return api});api.ready.catch(()=>{});api.destroy=()=>{if(destroyed)return;destroyed=true;impl?.destroy();stopTheme?.();if(activeChangeViews.get(host)===api)activeChangeViews.delete(host);if(host.canopyx===api)delete host.canopyx;host.removeAttribute("data-canopyx-mounted");clearError();for(const className of addedClasses)host.classList.remove(className);if(!hadClass&&!host.classList.length)host.removeAttribute("class")};ready.catch(showError);Object.freeze(api);activeChangeViews.set(host,api);host.classList.add("cx-host","cx-change-host");host.setAttribute("data-canopyx-mounted","true");host.canopyx=api;stopTheme=watchTheme(host);return api}}
|
|
1040
|
-
const CHANGES_API={createDiff:changeViewFactory("diff","createDiff"),createChangeList:changeViewFactory("changes","createChangeList"),createPropagation:changeViewFactory("propagation","createPropagation")};const CHANGE_VIEW_TYPES={diff:CHANGES_API.createDiff,changes:CHANGES_API.createChangeList,propagation:CHANGES_API.createPropagation};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)})}}}
|
|
1041
|
-
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};
|
|
1089
|
+
if(failure)throw failure;return impl[method](...args)};if(destroyed)throw cancelled();return loaded?invoke():ready.then(invoke)};api.ready=ready.then(()=>{if(destroyed)throw cancelled();return api});api.ready.catch(()=>{});api.destroy=()=>{if(destroyed)return;destroyed=true;impl?.destroy();stopTheme?.();if(activeChangeViews.get(host)===api)activeChangeViews.delete(host);if(host.canopyx===api)delete host.canopyx;host.removeAttribute("data-canopyx-mounted");clearError();for(const className of addedClasses)host.classList.remove(className);if(!hadClass&&!host.classList.length)host.removeAttribute("class")};ready.catch(showError);if(kind==="propagation")bindTimeSlider(api,options,kind,value=>CHANGES_IMPL.validatePropagation(value),showError);Object.freeze(api);activeChangeViews.set(host,api);host.classList.add("cx-host","cx-change-host");host.setAttribute("data-canopyx-mounted","true");host.canopyx=api;stopTheme=watchTheme(host);return api}}
|
|
1090
|
+
const CHANGES_API={createDiff:changeViewFactory("diff","createDiff"),createChangeList:changeViewFactory("changes","createChangeList"),createPropagation:changeViewFactory("propagation","createPropagation")};const CHANGE_VIEW_TYPES={diff:CHANGES_API.createDiff,changes:CHANGES_API.createChangeList,propagation:CHANGES_API.createPropagation};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,bindContextMenu});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)})}}}
|
|
1091
|
+
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};const GRID_COMPARE=definePart(DataGrid,"compare","dataset comparison",()=>({DataGrid,h,setText,fill,plural,cellValue,numberFormat,gridQueryStart,gridQuery}));const GRID_VIEW_INTENTS=definePart(DataGrid,"view-intents","view intents",()=>({DataGrid,h,setText,fill,gridButton,gridVariant,gridQuery,gridQueryStart,gridFreeze,GRID_FILTER_LABELS,GRID_REPORTING,watchOverlayTheme}));for(const name of["getViewIntent","validateViewIntent","previewViewIntent","applyViewIntent"]){const stub=DataGrid.prototype[name]=function(...args){return loadPart(GRID_VIEW_INTENTS).then(()=>{if(this.destroyed)throw new DOMException("Grid was destroyed.","AbortError");if(DataGrid.prototype[name]===stub)throw new Error("CanopyX: view intents did not install "+name+".");return this[name](...args)})}}
|
|
1092
|
+
DataGrid.prototype.getOnlyChanges=function(){return false};DataGrid.prototype.setOnlyChanges=function(...args){return loadPart(GRID_COMPARE).then(()=>{if(this.destroyed)throw new DOMException("Grid was destroyed.","AbortError");this.startDatasetCompare();return this.setOnlyChanges(...args)})};const DATA_VIEWS_INIT=DataGrid.prototype.initializeState;DataGrid.prototype.initializeState=function(){const parts=[this.view.compare&&loadPart(GRID_COMPARE),this.view.viewIntents&&loadPart(GRID_VIEW_INTENTS)].filter(Boolean);if(!parts.length)return DATA_VIEWS_INIT.call(this);return Promise.all(parts).then(()=>{if(this.destroyed)throw new DOMException("Grid was destroyed.","AbortError");if(this.view.compare)this.startDatasetCompare();return DATA_VIEWS_INIT.call(this)})};function gridVariant(button,variant){button.dataset.variant=variant;return button}
|
|
1093
|
+
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}
|
|
1042
1094
|
let keyboardInput=false;if(typeof document!=="undefined"){document.addEventListener("keydown",()=>{keyboardInput=true},true);document.addEventListener("pointerdown",()=>{keyboardInput=false},true)}
|
|
1043
1095
|
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}
|
|
1044
1096
|
function gridOption(value,text){const option=h("option");option.value=value;setText(option,text);return option}
|
|
1045
|
-
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
|
|
1097
|
+
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){const holds=element=>element&&(UI_TOOLS.owns?.(element,node)??element.contains(node));if(this.pendingColumnMenu&&!holds(this.pendingColumnMenu.anchor))this.pendingColumnMenu=null;for(const details of this.toolbar.querySelectorAll(".cx-columns[open]"))if(!holds(details))details.open=false;const popover=this.gridPopover;if(popover&&!holds(popover.panel)&&!holds(popover.anchor))this.closeGridPopover(false);const cellMenu=this.cellMenuPanel;if(cellMenu&&!holds(cellMenu)&&!holds(this.cellMenuHit?.cell))this.cellMenuBinding?.close("menus",false,true)},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");panel.setAttribute("aria-label",title);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(onOpen)onOpen(panel);flyout.open()}else{flyout.close();for(const inner of panel.querySelectorAll("details[open]"))inner.open=false}});details.addEventListener("keydown",event=>{if(event.key==="Escape"&&details.open){details.open=false;flyout.close();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))}
|
|
1046
1098
|
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\})/){
|
|
1047
1099
|
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}))}
|
|
1048
|
-
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)
|
|
1049
|
-
this.
|
|
1050
|
-
if(
|
|
1100
|
+
DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridRegexSearch:"Search pattern",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){input.setCustomValidity("");return}
|
|
1101
|
+
this.setQuery(gridQueryStart(this.queryForChange(),{search:input.value})).then(()=>input.setCustomValidity(""))
|
|
1102
|
+
.catch(error=>{if(!this.destroyed&&this.queryForChange().searchMode==="regex"){input.setCustomValidity(error.message);input.reportValidity()}})};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?
|
|
1103
|
+
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]||filter.operator;const condition=this.filterCaption?.(filter)||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})})}
|
|
1104
|
+
(query.tupleFilters||[]).forEach((tuple,index)=>{const condition=tuple.columns.map(field=>this.columns.find(column=>column.id===field.column).title).join(", ")+
|
|
1105
|
+
(tuple.exclude?" ∉ ":" ∈ ")+ JSON.stringify(tuple.values);chip(condition,fill(this.labels.gridRemoveCondition,{condition}),"tuple-"+index,()=>{const tuples=(this.queryForChange().tupleFilters||[]).slice(),at=tuples.findIndex(item=>JSON.stringify(item)===JSON.stringify(tuple));if(at>=0){tuples.splice(at,1);apply({tupleFilters:tuples})}})});if(query.search||query.filters.length||query.filterGroup||query.tupleFilters?.length){const clear=gridButton(this.labels.gridClearAll,()=>{clear.focus({preventScroll:true});if(this.searchInput)this.searchInput.value="";apply({search:"",filters:[],filterGroup:undefined,tupleFilters:undefined})});clear.classList.add("cx-link");clear.setAttribute("aria-label",this.labels.gridClearQuery);clear.setAttribute("data-cx-condition","clear");this.queryChips.appendChild(clear)}
|
|
1051
1106
|
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})}
|
|
1052
1107
|
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(()=>{})))}
|
|
1053
1108
|
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())}}
|
|
1054
1109
|
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)}
|
|
1055
1110
|
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(()=>
|
|
1056
1111
|
this.runGridHook(signal=>this.opts.onChart({query:this.getQuery(),selection:this.getSelectionDescriptor?.()||
|
|
1057
|
-
{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));
|
|
1058
|
-
|
|
1059
|
-
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)}}
|
|
1112
|
+
{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));const recordDetails=this.toolbar.querySelector(".cx-grid-details");if(recordDetails){recordDetails.prepend(gridIcon("columns"));recordDetails.classList.add("cx-btn","ghost")}
|
|
1113
|
+
overflow.panel.append(recordDetails,this.toolbar.querySelector(".cx-grid-print"));overflow.details.addEventListener("toggle",()=>{if(overflow.details.open)this.syncPrintAvailability()},{signal});(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}
|
|
1114
|
+
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(){this.syncPrintAvailability();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)}}
|
|
1060
1115
|
for(const button of this.selectionBar.querySelectorAll("button"))button.disabled=!!this.pendingQuery||!this.acceptedQuery;this.syncGridStatus?.()},updateGridPhone(){const phone=this.host.clientWidth<=640,mode=this.layoutMode||"fit",autoRows=mode!=="cards"&&(this.authoredAutoRows||phone&&mode==="fit");this.host.classList.toggle("cx-phone",phone);if(phone===this.phone&&autoRows===this.autoRows)return;const anchor=this.gridRowAnchor();const changedRows=autoRows!==this.autoRows;this.phone=phone;this.autoRows=autoRows;if(changedRows){this.rowOffsets=null;this.rowHeights?.clear()}
|
|
1061
1116
|
this.rowWidthShape=null;if(!this.autoRows&&!this.rowProjection){const bounds=this.rowBounds();this.total=Math.max(0,bounds.last-bounds.first+1)*this.rowH;this.syncScrollRange(this.rowTop(anchor.index)+ Math.min(anchor.offset,this.rowH-1))}
|
|
1062
|
-
this.version++;this.scheduleRender();this.placeMenus(phone);this.syncPinnedShadows()},syncPinnedShadows(){const overflow=this.grid.scrollWidth>this.grid.clientWidth+1,pins=overflow&&(this.pinnedLeftWidth||this.pinnedRightWidth);this.host.classList.toggle("cx-scroll-x",overflow);this.host.classList.toggle("cx-scroll-left",!!pins&&this.grid.scrollLeft>0);this.host.classList.toggle("cx-scroll-right",!!pins&&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))}
|
|
1063
|
-
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};
|
|
1064
|
-
|
|
1117
|
+
this.version++;this.scheduleRender();this.placeMenus(phone);this.syncPinnedShadows()},syncPinnedShadows(){const overflow=this.grid.scrollWidth>this.grid.clientWidth+1,pins=overflow&&(this.pinnedLeftWidth||this.pinnedRightWidth);this.host.classList.toggle("cx-scroll-x",overflow);this.host.classList.toggle("cx-scroll-left",!!pins&&this.grid.scrollLeft>0);this.host.classList.toggle("cx-scroll-right",!!pins&&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)||query.tupleFilters?.some(tuple=>tuple.columns.some(field=>field.column===id)))}
|
|
1118
|
+
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};const result={...query,filters:(query.filters||[]).filter(item=>item.column!==id),filterGroup:project(query.filterGroup)};if(query.tupleFilters){const tuples=query.tupleFilters.filter(tuple=>!tuple.columns.some(field=>field.column===id));if(tuples.length)result.tupleFilters=tuples;else delete result.tupleFilters}
|
|
1119
|
+
return result}
|
|
1120
|
+
Object.assign(DataGrid.prototype,{syncColumnMenuRoom(){for(const cell of this.headCells){const menu=cell.querySelector(".cx-grid-column-menu"),text=cell.querySelector(".cx-htext");if(!menu||!text)continue;const range=document.createRange();range.selectNodeContents(text);const box=menu.getBoundingClientRect(),caption=range.getBoundingClientRect();cell.classList.toggle("cx-menu-room",cell.matches(":dir(rtl)")?caption.left>=box.right+1:caption.right<=box.left-1)}},resolveColumnAnchor(id,anchor,position=false){const visible=element=>element?.isConnected&&element.getClientRects().length>0&&getComputedStyle(element).visibility==="visible";const key=CSS.escape(id||""),headerOrigin=!anchor||this.headRow.contains(anchor)||anchor.hasAttribute("data-cx-column-menu")||anchor.hasAttribute("data-cx-filter-column");if(position&&headerOrigin){const header=this.headCells.find(cell=>cell.dataset.cxColumn===id);if(visible(header))return header}
|
|
1121
|
+
const card=this.toolbar.querySelector('[data-cx-sort-column-menu="'+key+'"]');const filter=anchor?.hasAttribute("data-cx-filter-column")?this.headRow.querySelector('[data-cx-filter-column="'+key+'"]'):null;const menu=this.headRow.querySelector('[data-cx-column-menu="'+key+'"]');return[anchor,card,filter,menu].find(visible)||null},adoptColumnAnchor(id,attribute,anchor){const open=this.gridPopover;if(open?.columnId!==id||!open.anchor?.hasAttribute(attribute))return;open.anchor.setAttribute("aria-expanded","false");open.anchor=anchor;anchor.setAttribute("aria-controls",open.panel.id);anchor.setAttribute("aria-expanded","true")},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)(this.resolveColumnAnchor(open.columnId,open.anchor)||this.grid).focus({preventScroll:true})},buildColumnMenus(){this.headerMenusLifetime?.abort();this.headerMenusLifetime=new AbortController();const signal=this.headerMenusLifetime.signal;this.headRow.addEventListener("pointerenter",()=>this.syncColumnMenuRoom(),{signal});const clearTouch=()=>this.headCells.forEach(cell=>cell.classList.remove("cx-menu-touched"));document.addEventListener("pointerdown",clearTouch,{capture:true,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-expanded","false");menu.setAttribute("aria-expanded","false");this.adoptColumnAnchor(id,"data-cx-filter-column",filter);this.adoptColumnAnchor(id,"data-cx-column-menu",menu);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");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"));if(this.tools.columnMenus!==false){let press,timer,held=false;const cancel=()=>{global.clearTimeout(timer);press=null};document.addEventListener("pointerdown",cancel,{capture:true,signal});const open=event=>{event.preventDefault();event.stopPropagation();if(press)held=true;cancel();if(this.gridPopover?.columnId!==id||!this.gridPopover.panel.classList.contains("cx-column-menu"))this.openColumnMenu(id,menu)};cell.addEventListener("pointerdown",event=>{cancel();held=false;if(event.pointerType!=="touch")return;cell.classList.add("cx-menu-touched");if(!event.isPrimary||event.button||event.target.closest(".cx-resize,.cx-grid-column-menu,.cx-grid-header-filter,.cx-column-move"))return;press={id:event.pointerId,x:event.clientX,y:event.clientY};timer=global.setTimeout(()=>{press=null;held=true;this.openColumnMenu(id,menu)},550)},{signal});global.addEventListener("pointermove",event=>{if(press?.id===event.pointerId&&Math.hypot(event.clientX-press.x,event.clientY-press.y)>8)cancel()},{signal});for(const type of["pointerup","pointercancel"])global.addEventListener(type,event=>{if(press?.id===event.pointerId)cancel()},{signal});global.addEventListener("scroll",cancel,{capture:true,signal});cell.addEventListener("mousedown",event=>{if(held)event.preventDefault()},{signal});cell.addEventListener("click",event=>{if(held){held=false;event.preventDefault();event.stopImmediatePropagation()}},{capture:true,signal});signal.addEventListener("abort",cancel,{once:true});cell.addEventListener("contextmenu",open,{signal});cell.addEventListener("keydown",event=>{held=false;cancel();if(event.key==="ContextMenu"||event.shiftKey&&event.key==="F10")open(event)},{signal})}}
|
|
1065
1122
|
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)}}
|
|
1066
1123
|
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)}
|
|
1067
1124
|
for(const column of[...grid.cols].reverse())if(column.effectivePin==="right"){column.pinOffset=end;end+=width(column)}
|
|
1068
1125
|
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)})}
|
|
1069
|
-
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,gridKeyCompare,gridFacetKey,columnNumericTone,columnValueTone,gridColumnProjection,gridCellRenderer,cellValue,cellText,foldText,reachable,gridMatchesGroup,gridDecorateButton,gridIcon,numberFormat,numberText,fill,announceMenu,applyTone,toneOf,badgeElement,isCellObject,RECORD_IMPL,GRID_REPORTING,overlayTheme,reportingRangeRows:typeof reportingRangeRows==="function"?reportingRangeRows:null}));function loadGridFilterEditor(){return loadPart(FILTER_EDITOR)}
|
|
1126
|
+
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,gridVariant,gridInputLabel,gridOption,gridScalar,gridDateInputMoment,gridQuery,gridQueryStart,GRID_FILTER_LABELS,gridResultShape,gridQueryRows,gridOrder,gridKeyCompare,gridFacetKey,columnNumericTone,columnValueTone,gridColumnProjection,gridCellRenderer,cellValue,cellText,foldText,reachable,gridMatchesGroup,gridDecorateButton,gridIcon,numberFormat,numberText,fill,announceMenu,applyTone,toneOf,badgeElement,isCellObject,RECORD_IMPL,GRID_REPORTING,overlayTheme,UI_IMPL,UI_TOOLS,reportingRangeRows:typeof reportingRangeRows==="function"?reportingRangeRows:null}));function loadGridFilterEditor(){return loadPart(FILTER_EDITOR)}
|
|
1070
1127
|
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())}}
|
|
1071
|
-
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
|
|
1072
|
-
catch(error){this.setNotice(this.labels.gridLoadFailed,true);this.emit("error",{error})}}
|
|
1073
|
-
|
|
1128
|
+
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){const request=this.pendingColumnMenu={anchor};try{await loadGridFilterEditor();if(!this.destroyed&&this.pendingColumnMenu===request){const target=this.resolveColumnAnchor(id,anchor);if(target)this[method](id,target)}}
|
|
1129
|
+
catch(error){if(!this.destroyed&&this.pendingColumnMenu===request){this.setNotice(this.labels.gridLoadFailed,true);this.emit("error",{error})}}
|
|
1130
|
+
finally{if(this.pendingColumnMenu===request)this.pendingColumnMenu=null}};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}
|
|
1131
|
+
const OFFICE_EXPORT_PART={name:"office-export",label:"Office export writers",parts:{},context:()=>({EXPORT_IMPL})};const officeExportWrite=(capture,context)=>loadPart(OFFICE_EXPORT_PART).then(()=>EXPORT_IMPL.office(capture,context));function officeExportFormats(grid){return[{id:"xlsx",label:grid.labels.gridDownloadXlsx,extension:"xlsx",values:"raw",mime:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",write:officeExportWrite},{id:"pdf",label:grid.labels.gridDownloadPdf,extension:"pdf",values:"raw",mime:"application/pdf",write:officeExportWrite}]}
|
|
1132
|
+
EXPORT_IMPL.formatsOf=function(grid){const list=grid.opts.exportFormats??grid.view.exportFormats,known=new Map(EXPORT_FORMATS);if(list!==undefined)for(const format of officeExportFormats(grid))if(!known.has(format.id))known.set(format.id,exportFormat(format));for(const format of grid.plugins.filter(item=>(grid.opts.plugins||[]).some(local=>local.name===item.name))
|
|
1133
|
+
.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 gridOperationActive(grid,signal){if(grid.destroyed||signal?.aborted)throw new DOMException("Grid cancelled.","AbortError")}
|
|
1134
|
+
const exportPartReady=()=>typeof loadExportPart==="function"?loadExportPart():Promise.resolve();function exportAs(grid,format,options={}){const{onProgress,onDiagnostic,includeMetadata,timeoutMs,download,...capture}=options;let provider,resolutionError;try{provider=EXPORT_IMPL.formatsOf(grid).find(item=>item.id===format)}
|
|
1074
1135
|
catch(error){resolutionError=error}
|
|
1075
|
-
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)})}
|
|
1076
|
-
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))
|
|
1136
|
+
if(capture.values===undefined&&provider?.values!==undefined)capture.values=provider.values;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)})}
|
|
1137
|
+
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(options?.pivot!==undefined&&typeof options.pivot!=="boolean")throw new Error("CanopyX: pivot table capture requires a boolean option.");if(options?.pivot)throw new Error("CanopyX: pivot table capture requires the reporting export bridge.");if(!this.view.reporting&&["fileName","locale","timeZone","onProgress"].some(key=>options?.[key]!==undefined))
|
|
1077
1138
|
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||
|
|
1078
|
-
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:
|
|
1139
|
+
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,tupleFilters:filtered?this.acceptedQuery.tupleFilters: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:
|
|
1079
1140
|
filtered?matched:total,selection,columns:columns.map(({id,title,kind,format,valueLabels})=>({id,title,kind,...(valueLabels?{valueLabels:{...valueLabels}}:{}),...(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)||
|
|
1080
1141
|
index<(this.cursor?this.pageOffset:0)||index>=this.rowCount())
|
|
1081
1142
|
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)
|
|
1082
1143
|
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)})}
|
|
1083
1144
|
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)
|
|
1084
|
-
.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:"
|
|
1085
|
-
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)},
|
|
1086
|
-
|
|
1145
|
+
.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:"CSV",gridCopy:"Copy",gridDetails:"Record details",gridDownloadXlsx:"Excel workbook (.xlsx)",gridDownloadPdf:"PDF",gridExportWorkbookHint:"Formatted, with groups and totals",gridExportPdfHint:"Paged report with repeating headings",gridExportCsvHint:"Plain text values (.csv)",gridExportCopy:"Copy to clipboard",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)}
|
|
1146
|
+
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)},exportFromMenu(action){return loadExportPart().then(()=>EXPORT_IMPL.exportFromMenu.call(this,action))},syncPrintAvailability(){for(const button of[this.printButton,this.exportPrintButton])if(button)
|
|
1147
|
+
button.disabled=!!this.exportControl?.panel?.cxExportBusy?.()||!this.defaultExportColumns().length},print(options){const{onProgress,maxRows=1000,timeoutMs,...capture}=options||{};if(onProgress!==undefined&&typeof onProgress!=="function")throw new Error("CanopyX: print progress needs a callback.");const layout=this.layoutMode==="cards"?"stacked":"table";const tokenColumns=new Set();let template=layout==="stacked"&&this.view.cardTemplate?JSON.parse(JSON.stringify(this.view.cardTemplate)):null,projection=null,capturedOptions=capture;if(template&&typeof this.opts.onPrint!=="function"){projection=capture.columns===undefined?this.defaultExportColumns().map(column=>column.id):
|
|
1148
|
+
Array.isArray(capture.columns)?capture.columns.slice():null;if(projection?.length){const columns=projection.slice();for(const[slot,text]of Object.entries(template)){if(!projection.includes(slot)){delete template[slot];continue}
|
|
1149
|
+
tokenColumns.add(slot);const inputs=[...text.matchAll(/\{([^{}]+)\}/g)].map(match => match[1]);
|
|
1150
|
+
if(inputs.some(id=>!this.columns.some(column=>column.id===id&&column.exportable!==false))){template[slot]=null;continue}
|
|
1151
|
+
for(const id of inputs){tokenColumns.add(id);if(!columns.includes(id))columns.push(id)}}
|
|
1152
|
+
capturedOptions={...capture,columns}}}
|
|
1153
|
+
const exported=this.prepareExport(capturedOptions);const cardColumns=layout==="stacked"?(projection||exported.request.columns.map(column=>column.id)).map(id=>this.columns.find(column=>column.id===id)):[];const cardRoles=cardColumns.map((column,index)=>VIEW_MODES.cardRole(column,index,cardColumns));if(projection)GRID_REPORTING.captureCardText?.(exported.request,tokenColumns);return this.runGridHook(async signal=>{await loadExportPart();const progress=(phase,completed)=>onProgress?.(Object.freeze({phase,completed,total:exported.request.recordCount}));const snapshot=await EXPORT_IMPL.snapshot(this,exported,maxRows,signal,progress);if(typeof this.opts.onPrint==="function")await this.opts.onPrint({snapshot,signal,layout});else await EXPORT_IMPL.printSnapshot(this,snapshot,signal,layout,template,projection,cardRoles);gridOperationActive(this,signal);progress("ready",snapshot.rows.length)},capture.signal,exportTimeout(this,{timeoutMs}))},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"}));const print=this.printButton=gridButton(this.labels.gridPrint,()=>Promise.resolve().then(()=>this.exportControl?
|
|
1154
|
+
this.exportFromMenu((options,onProgress)=>this.print({...options,onProgress})):this.print({scope:"filtered"}))
|
|
1155
|
+
.catch(error=>this.emit("error",{error})));print.classList.add("cx-grid-print");print.hidden=this.tools.export!==false;this.toolbar.appendChild(print);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())}}
|
|
1156
|
+
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.hiddenViewColumns=function(){return this.columns.filter(column=>!column.hidden&&(column.responsiveHidden||column.fitHidden))};DataGrid.prototype.phoneLineColumns=function(){if(this.host.clientWidth>640||(this.layoutMode||"fit")!=="fit")return[];const hidden=this.hiddenViewColumns().sort((a,b)=>gridRank(a)- gridRank(b));const compact=this.columns.filter(column=>!column.hidden&&!column.responsiveHidden&&!column.fitHidden).length<=2;return hidden.filter(column=>gridLevel(column)===0||compact).slice(0,2)};DataGrid.prototype.phoneRemainingColumns=function(row){const shown=this.phoneLineColumns(row);return this.hiddenViewColumns().filter(column=>!shown.includes(column))};DataGrid.prototype.hiddenFieldCount=function(row){return this.phoneRemainingColumns(row).length};DataGrid.prototype.hiddenFieldLabel=function(row){const count=this.hiddenFieldCount(row);return fill(plural(this.labels.hiddenColumns,count,this.lang),{count:numberFormat(this.lang).format(count)})};DataGrid.prototype.paintHiddenFields=function(slot,row,column){if(this.phone&&column.identity)slot.cell.title=cellText(row.cells[column.id],column,this.fmt)||"";const first=column===this.cols[0],fields=first&&this.phone?this.phoneLineColumns(row):[];let button=slot.hiddenFields;const count=first&&(this.phone||this.desktopHiddenFields)&&this.hiddenFieldCount(row);if(!count){button?.remove();slot.hiddenFields=null}
|
|
1087
1157
|
else{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}
|
|
1088
|
-
const label=this.hiddenFieldLabel(),titles=this.phoneRemainingColumns().map(item=>item.title).join(", ");button.dataset.cxRecord=row.id;button.setAttribute("aria-label",label+": "+titles);button.title=titles;setText(button,label);slot.hiddenFields=button}
|
|
1089
|
-
if(!first){slot.childSlots?.forEach(disposeHostSlot);slot.childSlots=null;slot.phoneLine?.remove();slot.phoneLine=null;if(slot.phonePrimary){slot.phonePrimary.replaceWith(...slot.phonePrimary.childNodes);slot.phonePrimary=null}
|
|
1090
|
-
return}
|
|
1158
|
+
const label=this.hiddenFieldLabel(row),titles=this.phoneRemainingColumns(row).map(item=>item.title).join(", ");button.dataset.cxRecord=row.id;button.setAttribute("aria-label",label+": "+titles);button.title=titles;setText(button,label);slot.hiddenFields=button}
|
|
1159
|
+
if(!first||!this.phone){slot.childSlots?.forEach(disposeHostSlot);slot.childSlots=null;slot.phoneLine?.remove();slot.phoneLine=null;if(slot.phonePrimary){slot.phonePrimary.replaceWith(...slot.phonePrimary.childNodes);slot.phonePrimary=null}
|
|
1160
|
+
if(first&&count)slot.cell.appendChild(button);return}
|
|
1091
1161
|
let primary=slot.phonePrimary;if(!primary||primary.parentNode!==slot.cell)primary=h("span","cx-phone-primary");for(const node of[...slot.cell.childNodes])if(node!==primary&&node!==slot.phoneLine&&node!==button)primary.appendChild(node);if(primary.parentNode!==slot.cell)slot.cell.prepend(primary);slot.phonePrimary=primary;if(!fields.length&&!count){slot.childSlots?.forEach(disposeHostSlot);slot.childSlots=null;slot.phoneLine?.remove();slot.phoneLine=null;return}
|
|
1092
1162
|
let line=slot.phoneLine;if(!line||line.parentNode!==slot.cell){line=h("span","cx-phone-fields");slot.cell.appendChild(line)}
|
|
1093
1163
|
slot.phoneLine=line;const shape=JSON.stringify(fields.map(item=>item.id));if(line.dataset.cxFields!==shape){slot.childSlots?.forEach(disposeHostSlot);line.replaceChildren();line.dataset.cxFields=shape;slot.childSlots=[];for(const field of fields){const item=h("span","cx-phone-field"),label=h("span","cx-phone-field-title"),value=h("span","cx-phone-value");setText(label,field.title+":");item.append(label,value);line.appendChild(item);const content=value.appendChild(h("span","cx-content")),textSpan=content.appendChild(h("span","cx-text"));slot.childSlots.push({cell:value,content,base:"cx-phone-value",cls:null,title:null,badges:[],mode:"default",textSpan,text:textSpan.appendChild(document.createTextNode(""))})}}
|
|
1094
1164
|
if(count&&button.parentNode!==line)line.appendChild(button);fields.forEach((field,index)=>{this.paintSlot(slot.childSlots[index],row,field,this.columns.indexOf(field),{level:0,expanded:false});slot.childSlots[index].cell.title=cellText(row.cells[field.id],field,this.fmt)||""})};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([]),getLoadedRows:ids=>{const wanted=new Set(Array.isArray(ids)?ids:[]),rows=[];for(const row of grid.rowCache.values())if(wanted.has(row.key))rows.push({id:row.key,cells:JSON.parse(JSON.stringify(row.h.cells||{}))});return rows},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&&
|
|
1095
|
-
{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"])
|
|
1165
|
+
{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","getOnlyChanges","setOnlyChanges","setColumnWidth","autoFitColumn","autoFitColumns","autoSizeColumns","setColumnPriority","resetColumns","setColumnPinned","setColumnVisible","setColumnOrder","getState","setState","resetView","getSavedViews","saveView","applySavedView","removeSavedView","setTimeZone","setSelection","prepareExport","toCsv","copy","exportToHost","exportAs","print","openDetails","openRecord","scrollToRow","retry","closeMenus","getFormatRules","setFormatRules","openFormatRules","getCalculatedColumns","setCalculatedColumns","openCalculatedColumn","getLayout","setLayout","getMatches","setSearchHighlight","nextMatch","previousMatch",...GRID_EDIT_API,...GRID_LIVE_API,...GRID_CALENDAR_API,"getLiveState","getFacetPanel","setFacetPanelOpen"])
|
|
1096
1166
|
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)}}}
|
|
1097
|
-
GRID_REPORTING.api?.(api,grid);
|
|
1098
|
-
|
|
1167
|
+
GRID_REPORTING.api?.(api,grid);for(const name of["getViewIntent","validateViewIntent","previewViewIntent","applyViewIntent"])
|
|
1168
|
+
api[name]=(...args)=>{try{return Promise.resolve(grid[name](...args))}catch(error){return Promise.reject(error)}};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}
|
|
1169
|
+
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();if(this.view.stickyHeader!==undefined&&typeof this.view.stickyHeader!=="boolean")throw new Error("CanopyX: stickyHeader is a boolean.");readCardTemplate(this.view.cardTemplate,this.columns,this.layoutOptions);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.view.stickyHeader?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}});const FLOW_GRID_VIEWS=Object.freeze({grid:createDataGrid,tree:createTreeGrid,get reporting(){return typeof GRID_REPORTING==="object"&&typeof GRID_REPORTING.api==="function"}});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})}
|
|
1099
1170
|
function checkBranch(){return{value:null,mask:0,children:new Map()}}
|
|
1100
1171
|
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)}
|
|
1101
1172
|
if(branch.value!==null)throw new Error("CanopyX: a checking path cannot have duplicate intents.");branch.value=item.checked}
|
|
@@ -1175,21 +1246,14 @@ cancelInteraction(){const operation=this.pendingInteraction;if(!operation)return
|
|
|
1175
1246
|
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()}
|
|
1176
1247
|
withInteractionReplacement(work){this.interactionReplacementDepth=(this.interactionReplacementDepth||0)+ 1;try{return work()}
|
|
1177
1248
|
finally{this.interactionReplacementDepth--;if(!this.interactionReplacementDepth){const controllers=this.interactionAbortQueue;this.interactionAbortQueue=null;if(controllers)for(const controller of controllers)controller.abort()}}}
|
|
1178
|
-
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()
|
|
1249
|
+
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()}
|
|
1250
|
+
buildToolbar(){super.buildToolbar();if(!(this.opts.onEdit&&this.columns.some(column=>column.editable)||
|
|
1251
|
+
this.opts.onAction&&this.view.contextActions?.length||this.view.dragDrop&&this.opts.onDrop))return;const signal=this.interactionUiLifetime.signal;this.actionMenuReady=loadPart(SHARED_PARTS["ui-menu"]).then(()=>{if(!signal.aborted)UI_IMPL.treeActions.start.call(this,hostControl)});this.toolsReady=Promise.all([this.toolsReady,this.actionMenuReady]).catch(error=>{if(!signal.aborted)this.emit("error",{error})})}
|
|
1179
1252
|
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))}})}
|
|
1180
|
-
openInteractionPanel(w,anchor,role,label){
|
|
1181
|
-
|
|
1182
|
-
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,"")}
|
|
1183
|
-
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}
|
|
1253
|
+
openInteractionPanel(w,anchor,role,label){return UI_IMPL.treeActions.openInteractionPanel.call(this,w,anchor,role,label)}
|
|
1254
|
+
closeInteractionPanel(restoreFocus=true){return UI_IMPL.treeActions?.closeInteractionPanel.call(this,restoreFocus)||null}
|
|
1184
1255
|
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})}}
|
|
1185
|
-
|
|
1186
|
-
beginEdit(id,columnId){if(this.destroyed||this.interactionClosing||this.initializing||this.restoring||!this.opts.onEdit)return false;const w=this.interactionNode(id),index=this.columns.findIndex(column=>column.id===columnId),column=this.columns[index];if(!w||!column||!column.editable||column.hidden||!this.eligible("canEdit",w.h,column))return false;const row=this.rendered.get(w);if(!row||!row.isConnected)return false;const label=fill(this.labels.editValue,{column:column.title});const panel=this.openInteractionPanel(w,row,"dialog",label);if(!panel)return false;const controller=this.editorController=new AbortController();const raw=this.cellOf(w.h,column,index),value=isCellObject(raw)?raw.value:raw;const input=h("input","cx-editor");input.type=column.kind==="number"||typeof value==="number"?"number":typeof value==="boolean"?"checkbox":"text";if(input.type==="number"){input.step="any";input.required=true}
|
|
1187
|
-
input.checked=value===true;input.value=value==null?"":String(value);input.setAttribute("aria-label",label);input.addEventListener("input",()=>input.setCustomValidity(""));const controls=h("div","cx-interaction-buttons");const save=h("button","cx-save"),cancel=h("button");save.type=cancel.type="button";setText(save,this.labels.saveEdit);setText(cancel,this.labels.cancelEdit);const title=h("div","cx-interaction-title");setText(title,label);controls.append(save,cancel);panel.append(title,input,controls);const submit=async()=>{if(save.disabled)return;let next=input.type==="checkbox"?input.checked:input.value;if(input.type==="number"){if(!input.value.trim()||!Number.isFinite(Number(input.value))){input.setCustomValidity(fill(this.labels.invalidEdit,{column:column.title}));input.reportValidity();return}
|
|
1188
|
-
next=Number(input.value)}
|
|
1189
|
-
input.setCustomValidity("");input.disabled=save.disabled=true;cancel.focus();const accepted=await this.performInteraction(()=>this.requestEdit(id,columnId,next,{signal:controller.signal,reason:"editor"}),controller);if(this.interactionPanel!==panel)return;if(accepted)this.closeInteractionPanel();else{input.disabled=save.disabled=false;input.focus()}};save.addEventListener("click",submit);cancel.addEventListener("click",()=>this.closeInteractionPanel());panel.addEventListener("keydown",event=>{if(event.key==="Escape"){event.preventDefault();event.stopPropagation();this.closeInteractionPanel()}
|
|
1190
|
-
else if(event.key==="Enter"&&event.target===input){event.preventDefault();submit()}});this.placeInteractionPanel(row);input.focus();if(input.type!=="checkbox")input.select();return true}
|
|
1191
|
-
onKey(event){if(!event.defaultPrevented&&!event.isComposing&&!hostControl(event.target)){const row=event.target.closest(".cx-row"),w=row&&row._w;if(w&&!w.status&&!w.h.detail){if(event.key==="ContextMenu"||event.key==="F10"&&event.shiftKey){if(this.openActionMenu(w,row)){event.preventDefault();event.stopPropagation();return}}
|
|
1192
|
-
if(event.key==="Enter"&&event.altKey){const cell=event.target.closest(".cx-cell"),column=cell&&cell.getAttribute("data-cx-column");const editable=this.columns.find(item=>item.editable&&!item.hidden&&this.eligible("canEdit",w.h,item));if(this.beginEdit(w.key,column||editable&&editable.id)){event.preventDefault();event.stopPropagation();return}}}}
|
|
1256
|
+
onKey(event){if(!event.defaultPrevented&&!event.isComposing&&!hostControl(event.target)){const row=event.target.closest(".cx-row"),w=row&&row._w;if(w&&!w.status&&!w.h.detail){if(event.key==="Enter"&&event.altKey){const cell=event.target.closest(".cx-cell"),column=cell&&cell.getAttribute("data-cx-column");const editable=this.columns.find(item=>item.editable&&!item.hidden&&this.eligible("canEdit",w.h,item));if(this.beginEdit(w.key,column||editable&&editable.id)){event.preventDefault();event.stopPropagation();return}}}}
|
|
1193
1257
|
super.onKey(event)}
|
|
1194
1258
|
destroy(){this.interactionClosing=true;this.closeInteractionPanel(false);if(this.interactionUiLifetime)this.interactionUiLifetime.abort();super.destroy();this.interactionUiLifetime=null}};TreeGrid=class extends TreeGrid{cachePinned(w){return super.cachePinned(w)||this.panelNode===w||!!(this.moveNodes&&this.moveNodes.includes(w))}
|
|
1195
1259
|
refreshCounts(ids){return this.withInteractionReplacement(()=>super.refreshCounts(ids))}
|
|
@@ -1204,19 +1268,9 @@ const result=super.reconcileList(parent,...args);focus=focus||this.interactionFo
|
|
|
1204
1268
|
retireWrapper(w){return this.withInteractionReplacement(()=>{if(this.panelNode===w)this.interactionFocusHandoff=this.closeInteractionPanel(false)||this.interactionFocusHandoff;if(this.moveNodes&&this.moveNodes.includes(w))this.moveNodes=null;return super.retireWrapper(w)})}
|
|
1205
1269
|
applyCounts(doc,fromRefresh=false){return this.withInteractionReplacement(()=>{const panel=this.interactionPanel,node=this.panelNode,moving=this.moveNodes;const result=super.applyCounts(doc,fromRefresh);const focus=node&&this.interactionPanel===panel&&doc.items.some(item=>item.id===node.key)?this.closeInteractionPanel(false):null;if(moving&&this.moveNodes===moving&&doc.items.some(item=>moving.some(w=>w.key===item.id)))this.moveNodes=null;const handoff=focus||this.interactionFocusHandoff;this.interactionFocusHandoff=null;this.restoreInteractionFocus(handoff);return result})}
|
|
1206
1270
|
applyStatePresentation(state){const focus=this.closeInteractionPanel(false);this.moveNodes=null;this.cancelInteraction();const result=super.applyStatePresentation(state);this.restoreInteractionFocus(focus);return result}
|
|
1207
|
-
|
|
1208
|
-
markMove(
|
|
1209
|
-
|
|
1210
|
-
async moveTo(w,placement,reason="menu"){if(!this.moveTarget(w,placement))return false;const nodes=this.moveNodes;this.moveNodes=null;this.cacheDirty=true;return this.performInteraction(()=>this.requestDrop(nodes.map(node=>node.key),w.key,placement,{reason}))}
|
|
1211
|
-
openActionMenu(w,anchor){if(this.destroyed||this.interactionClosing||this.initializing||this.restoring||!w||w.status||w.h.detail||!anchor||!anchor.isConnected)return false;const items=[];if(this.opts.onEdit)for(const column of this.columns){if(column.editable&&!column.hidden)items.push({label:fill(this.labels.editValue,{column:column.title}),disabled:!this.eligible("canEdit",w.h,column),run:()=>this.beginEdit(w.key,column.id)})}
|
|
1212
|
-
if(this.opts.onAction)for(const action of this.interactions.actions)items.push({label:action.label,disabled:!this.eligible("isActionEnabled",w.h,action),run:()=>this.performInteraction(()=>this.requestAction(w.key,action.id,{reason:"menu"}))});if(this.view.dragDrop&&this.opts.onDrop){items.push({label:this.labels.markMove,disabled:!this.moveSources(w),run:()=>this.markMove(w)});if(this.moveNodes){for(const[placement,label]of[["inside","moveHere"],["before","moveBefore"],["after","moveAfter"]])
|
|
1213
|
-
items.push({label:this.labels[label],disabled:!this.moveTarget(w,placement),run:()=>this.moveTo(w,placement)});items.push({label:this.labels.cancelMove,run:()=>{this.moveNodes=null;this.cacheDirty=true}})}}
|
|
1214
|
-
if(!items.length||this.destroyed||w._invalidated||this.keyMap.get(w.key)!==w)return false;const panel=this.openInteractionPanel(w,anchor,"menu",this.labels.actions);if(!panel)return false;const buttons=items.map(item=>{const button=h("button","cx-action-item");button.type="button";button.tabIndex=-1;button.setAttribute("role","menuitem");if(item.disabled)button.setAttribute("aria-disabled","true");setText(button,item.label);button.addEventListener("click",()=>{if(item.disabled)return;this.closeInteractionPanel();item.run()});panel.appendChild(button);return button});panel.addEventListener("keydown",event=>{const index=buttons.indexOf(document.activeElement);let target;if(event.key==="ArrowDown")target=buttons[(index+1)%buttons.length];else if(event.key==="ArrowUp")target=buttons[(index+buttons.length-1)%buttons.length];else if(event.key==="Home")target=buttons[0];else if(event.key==="End")target=buttons[buttons.length-1];else if(event.key==="Escape")this.closeInteractionPanel();else if(event.key==="Tab"){this.closeInteractionPanel();return}
|
|
1215
|
-
else return;event.preventDefault();event.stopPropagation();if(target)target.focus()});this.placeInteractionPanel(anchor);buttons[0].focus();return true}
|
|
1216
|
-
onKey(event){if(!event.defaultPrevented&&!event.isComposing&&!hostControl(event.target)&&event.ctrlKey&&event.shiftKey&&!event.altKey){const row=event.target.closest(".cx-row"),w=row&&row._w;if(event.key.toLowerCase()==="x"&&this.markMove(w)){event.preventDefault();event.stopPropagation();return}
|
|
1217
|
-
if(event.key.toLowerCase()==="v"&&this.moveTarget(w)){event.preventDefault();event.stopPropagation();this.moveTo(w,"inside","keyboard");return}}
|
|
1218
|
-
super.onKey(event)}
|
|
1219
|
-
destroy(){this.moveNodes=null;super.destroy()}};TreeGrid=class extends TreeGrid{build(){super.build();const signal=this.interactionUiLifetime.signal;this.grid.addEventListener("scroll",()=>this.clearDropRow(),{signal,passive:true});this.grid.addEventListener("dragstart",event=>{const row=event.target.closest(".cx-row");if(!row||hostControl(event.target)||!event.dataTransfer||!this.markMove(row._w)){event.preventDefault();return}
|
|
1271
|
+
onKey(event){if(UI_IMPL.treeActions?.onKey.call(this,event,hostControl))return;super.onKey(event)}
|
|
1272
|
+
destroy(){this.moveNodes=null;super.destroy()}};for(const name of["moveSources","markMove","moveTarget","moveTo","actionMenuEligible","actionMenuItems","openActionMenu","beginEdit","performInteraction","placeInteractionPanel"]){TreeGrid.prototype[name]=function(...args){if(name==="beginEdit")args=[args[0],args[1],isCellObject];const invoke=()=>UI_IMPL.treeActions[name].apply(this,args);if(UI_IMPL.treeActions)return invoke();if(this.destroyed||this.initializing||this.restoring||["moveSources","markMove","moveTarget","actionMenuEligible"].includes(name))return false;return loadPart(SHARED_PARTS["ui-menu"]).then(()=>this.destroyed?false:invoke())}}
|
|
1273
|
+
TreeGrid=class extends TreeGrid{build(){super.build();const signal=this.interactionUiLifetime.signal;this.grid.addEventListener("scroll",()=>this.clearDropRow(),{signal,passive:true});this.grid.addEventListener("dragstart",event=>{const row=event.target.closest(".cx-row");if(!row||hostControl(event.target)||!event.dataTransfer||!this.markMove(row._w)){event.preventDefault();return}
|
|
1220
1274
|
this.dragLease=this.moveNodes;event.dataTransfer.effectAllowed="move";event.dataTransfer.setData("application/x-canopyx-move",this.id)},{signal});this.grid.addEventListener("dragover",event=>{const row=event.target.closest(".cx-row");const placement=row&&this.dropPlacement(event,row);if(this.dragLease&&event.dataTransfer&&Array.from(event.dataTransfer.types).includes("application/x-canopyx-move")&&row&&this.moveTarget(row._w,placement)){event.preventDefault();event.dataTransfer.dropEffect="move";if(this.dropRow!==row){this.clearDropRow();this.dropRow=row;row.classList.add("cx-drop-target")}
|
|
1221
1275
|
row.setAttribute("data-cx-drop-placement",placement)}else{this.clearDropRow();if(event.dataTransfer)event.dataTransfer.dropEffect="none"}},{signal});this.grid.addEventListener("drop",event=>{const row=event.target.closest(".cx-row");const placement=row&&this.dropPlacement(event,row);if(!this.dragLease||!event.dataTransfer||event.dataTransfer.getData("application/x-canopyx-move")!==this.id||!row||!this.moveTarget(row._w,placement))return;event.preventDefault();this.clearDropRow();this.dragLease=null;this.moveTo(row._w,placement,"pointer")},{signal});this.grid.addEventListener("dragend",()=>{this.clearDropRow();if(this.dragLease){this.moveNodes=null;this.cacheDirty=true}
|
|
1222
1276
|
this.dragLease=null},{signal})}
|
|
@@ -1253,9 +1307,9 @@ fields.operator.appendChild(gridOption("between",this.labels.gridBetween));upper
|
|
|
1253
1307
|
fields.type.value!=="string"||!["eq","ne"].includes(fields.operator.value);choices.textContent="";choices.appendChild(gridOption("",this.labels.gridChooseCategory));for(const value of categories&&categories.categories||[])choices.appendChild(gridOption(value,value));booleanField.hidden=column.kind!=="text"||fields.type.value!=="boolean"||!["eq","ne"].includes(fields.operator.value);if(!booleanField.hidden){boolean.value=["true","false"].includes(fields.input.value)?fields.input.value:"true";fields.input.value=boolean.value}
|
|
1254
1308
|
fields.input.closest("label").hidden=!booleanField.hidden||["isNull","isNotNull","in"].includes(fields.operator.value)};fields.column.addEventListener("change",refresh);fields.operator.addEventListener("change",refresh);fields.type.addEventListener("change",refresh);upper.addEventListener("input",()=>{fields.edited=true});choices.addEventListener("change",()=>{fields.input.value=choices.value;fields.edited=true});boolean.addEventListener("change",()=>{fields.input.value=boolean.value;fields.edited=true});refresh();if(range){fields.operator.value="between";upper.value=upper.type==="datetime-local"?gridDateInput(predicate.values[1],this.local):String(predicate.values[1]);refresh();fields.rangeOriginal={column:predicate.column,values:predicate.values.slice(),inputs:[fields.input.value,upper.value]}}},readGridFilter(row){const fields=row._filter;if(!this.view.reporting||fields.operator.value!=="between")return REPORT_FILTER_BASE.readGridFilter.call(this,row);const column=this.columns.find(item=>item.id===fields.column.value);const original=fields.rangeOriginal,inputs=[fields.input.value,fields.upper.value];const values=inputs.map((raw,index)=>original&&original.column===column.id&&raw===original.inputs[index]?
|
|
1255
1309
|
original.values[index]:reportingRangeValue(raw,column,this.labels,this.local));if(gridScalar(values[0],column)>gridScalar(values[1],column))throw new Error(this.labels.gridInvalidRange);return[{column:column.id,operator:"ge",values:[values[0]]},{column:column.id,operator:"le",values:[values[1]]}]}})};function reportingColumnFiltered(query,id){return gridColumnFiltered(query,id)}
|
|
1256
|
-
DataGrid.prototype.syncReportingFilterHeaders=function(){};DataGrid.prototype.openReportingColumnFilter=function(id){return this.openColumnFilter(id,this.headRow.querySelector('[data-cx-filter-column="'+CSS.escape(id)+'"]:not([hidden]),[data-cx-column-menu="'+CSS.escape(id)+'"]'))};DataGrid.prototype.focusReportingFilterHeader=function(){this.headRow.querySelector('[data-cx-filter-column="'+CSS.escape(this.reportingFilterReturn||"")+'"]:not([hidden]),[data-cx-column-menu="'+CSS.escape(this.reportingFilterReturn||"")+'"]')?.focus({preventScroll:true})};const reportingFacetKey=gridFacetKey;const reportingFacetPage=(input,grid,request)=>validateGridFacetPage(input,grid.view,request,grid.snapshot);const reportingFacetProjection=(query,column)=>gridColumnProjection(query,column);const FACET_INLINE_SOURCE=inlineGridSource;inlineGridSource=view=>{const source=FACET_INLINE_SOURCE(view);if(!view.reporting?.facetColumns?.length)return source;const columns=new Map(view.columns.map(column=>[column.id,column]));source.facet=(request,options)=>{if(options.signal.aborted)throw new DOMException("Facet request cancelled.","AbortError");const
|
|
1257
|
-
reportingFacetProjection(request.query,column.id);const rows=gridQueryRows(view,query,columns,false),buckets=new Map(),needle=foldText(request.search);for(const row of rows){const scalar=gridScalar(row.cells[column.id],
|
|
1258
|
-
const items=[...buckets.values()].filter(item
|
|
1310
|
+
DataGrid.prototype.syncReportingFilterHeaders=function(){};DataGrid.prototype.openReportingColumnFilter=function(id){return this.openColumnFilter(id,this.headRow.querySelector('[data-cx-filter-column="'+CSS.escape(id)+'"]:not([hidden]),[data-cx-column-menu="'+CSS.escape(id)+'"]'))};DataGrid.prototype.focusReportingFilterHeader=function(){this.headRow.querySelector('[data-cx-filter-column="'+CSS.escape(this.reportingFilterReturn||"")+'"]:not([hidden]),[data-cx-column-menu="'+CSS.escape(this.reportingFilterReturn||"")+'"]')?.focus({preventScroll:true})};const reportingFacetKey=gridFacetKey;const reportingFacetPage=(input,grid,request)=>validateGridFacetPage(input,grid.view,request,grid.snapshot);const reportingFacetProjection=(query,column)=>gridColumnProjection(query,column);const FACET_INLINE_SOURCE=inlineGridSource;inlineGridSource=view=>{const source=FACET_INLINE_SOURCE(view);if(!view.reporting?.facetColumns?.length)return source;const columns=new Map(view.columns.map(column=>[column.id,column]));source.facet=(request,options)=>{if(options.signal.aborted)throw new DOMException("Facet request cancelled.","AbortError");const declared=view.columns.find(item=>item.id===request.column),column=request.bucket?{...declared,kind:"number"}:declared,query=request.includeCurrentFilter?request.query:
|
|
1311
|
+
reportingFacetProjection(request.query,column.id);const rows=gridQueryRows(view,query,columns,false),buckets=new Map(),needle=foldText(request.search);for(const row of rows){const scalar=gridScalar(row.cells[column.id],declared),value=request.bucket?GRID_BUCKETS.key(request.bucket,scalar):column.kind==="datetime"&&scalar!==null?new Date(scalar).toISOString():scalar;const key=reportingFacetKey(value,column),existing=buckets.get(key);if(existing){existing.count++;if(typeof value==="string"&&value<existing.value)existing.value=value}else buckets.set(key,{value,count:1})}
|
|
1312
|
+
const items=[...buckets.values()].filter(item=>!needle||foldText(request.bucket&&item.value!==null?GRID_BUCKETS.label(request.bucket,item.value,"en"):cellText(item.value,column,formatting(undefined,false,DEFAULT_LABELS))).includes(needle));items.sort((a,b)=>{if(a.value===null||b.value===null)return a.value===b.value?0:a.value===null?1:-1;if(column.kind!=="text")return gridOrder(gridScalar(a.value,column),gridScalar(b.value,column));if(column.valueOrder?.length)return gridKeyCompare(a.value,b.value,column,"asc");if(typeof a.value!==typeof b.value)return typeof a.value==="boolean"?-1:1;return gridOrder(typeof a.value==="string"?foldText(a.value):a.value,typeof b.value==="string"?foldText(b.value):b.value)});return{protocol:PROTOCOL,type:"gridFacetPage",view:view.id,revision:gridInlineRevision(view),column:column.id,search:request.search,includeCurrentFilter:request.includeCurrentFilter,matched:rows.length,totalValues:items.length,truncated:items.length>request.limit,items:items.slice(0,request.limit),...(request.bucket?{bucket:request.bucket}:{})}};return source};const LAYOUT_VALIDATE=GRID_REPORTING.validate,LAYOUT_PREPARE=DataGrid.prototype.prepareState;GRID_REPORTING.api=(api,grid)=>{api.autoFitColumn=id=>GRID_REPORTING.fitColumn(grid,id)};function reportingLayout(grid,column){const declared=column.kind?column:grid.columns.find(item=>item.id===column.id)||column,policy=grid.columnLayouts.get(column.id)||{};return{minimum:declared.minWidth??policy.minimum??40,maximum:declared.maxWidth??policy.maximum??MAX_COLUMN_WIDTH,flex:declared.flexWeight??(typeof declared.flex==="number"?declared.flex:policy.flex||0),priority:declared.priority===undefined?policy.priority:gridRank(declared),alwaysVisible:declared.alwaysVisible??policy.alwaysVisible,pin:declared.pinned===undefined?policy.pin:declared.pinned||"none"}}
|
|
1259
1313
|
function reportingWidth(grid,column,width){const policy=reportingLayout(grid,column);return Math.min(policy.maximum,Math.max(policy.minimum,width))}
|
|
1260
1314
|
GRID_REPORTING.validate=view=>{LAYOUT_VALIDATE(view);const options=view.reporting;if(options.columnReorder!==undefined&&typeof options.columnReorder!=="boolean")throw new Error("CanopyX: columnReorder must be boolean.");if(options.layout===undefined)return;if(!options.layout||typeof options.layout!=="object"||Array.isArray(options.layout))throw new Error("CanopyX: layout is keyed by column ID.");for(const[id,policy]of Object.entries(options.layout)){const column=view.columns.find(item=>item&&item.id===id);if(!column||!policy||typeof policy!=="object"||Array.isArray(policy)||
|
|
1261
1315
|
!Number.isInteger(policy.minimum)||policy.minimum<40||!Number.isInteger(policy.maximum)||
|
|
@@ -1281,7 +1335,7 @@ throw new Error("CanopyX: invalid responsive flag or minimum center width.");for
|
|
|
1281
1335
|
policy.alwaysVisible!==undefined&&typeof policy.alwaysVisible!=="boolean")
|
|
1282
1336
|
throw new Error("CanopyX: invalid reporting placement policy.")}};DataGrid.prototype.prepareState=function(){const policies=this.view.reporting&&this.view.reporting.layout||{};for(const column of this.columns)column.pin=policies[column.id]?.pin??column.pin;return PLACEMENT_PREPARE.call(this)};gridStateColumn=column=>Object.assign(PLACEMENT_STATE_COLUMN(column),{pin:column.pin});gridStateColumns=(grid,input)=>{const result=PLACEMENT_STATE_COLUMNS(grid,input),sources=new Map(input.map(column=>[column.id,column]));for(const saved of result){const source=sources.get(saved.id)||saved;if(source.pin!==undefined&&!COLUMN_PINS.includes(source.pin))throw new Error("CanopyX: invalid saved column pin.");saved.pin=source.pin===undefined?reportingLayout(grid,saved).pin||"none":source.pin}
|
|
1283
1337
|
return result};GRID_REPORTING.api=(api,grid)=>{PLACEMENT_API(api,grid);api.setColumnPinned=(id,pin)=>grid.setColumnPinned(id,pin);api.getColumnLayout=()=>grid.columns.map(column=>{const hiddenReason=column.hidden?"user":column.responsiveHidden?"priority":column.fitHidden?
|
|
1284
|
-
grid.phone&&gridLevel(column)===1?"phone":"priority":null;return Object.freeze({id:column.id,requestedPin:column.pin,pin:column.effectivePin||"none",visible:!hiddenReason,hiddenReason,responsiveHidden:!!column.responsiveHidden,width:column.renderWidth||column.width})})};DataGrid.prototype.setColumnPinned=function(id,pin){const column=this.columns.find(item=>item.id===id);if(this.destroyed||!column||!COLUMN_PINS.includes(pin))throw new Error("CanopyX: pin needs a live grid, declared column and none, left or right.");if(this.cancelResize)this.cancelResize();const held=this.heldCell(),columnId=held&&this.cols[held.index]&&this.cols[held.index].id;column.pin=pin;this.buildHeader();this.syncColumnControls();this.render();this.restoreReportingFocus(held,columnId);this.stateChanged();this.emit("columnPin",{column:id,pin})};const PLACEMENT_WIDTHS=DataGrid.prototype.applyWidths,PLACEMENT_CELL=DataGrid.prototype.newCell;DataGrid.prototype.visibleColumns=function(){const requested=this.columns.filter(column=>!column.hidden),options=this.view.reporting||{};for(const column of this.columns)column.responsiveHidden=false;if(options.responsive&&this.grid&&this.grid.clientWidth&&(this.layoutMode||"fit")==="fit"){let total=requested.reduce((sum,column)=>sum+reportingWidth(this,column,column.width),0),remaining=requested.length;const candidates=requested.filter(column=>!reportingLayout(this,column).alwaysVisible).sort((a,b)=>
|
|
1338
|
+
grid.phone&&gridLevel(column)===1?"phone":"priority":null;return Object.freeze({id:column.id,requestedPin:column.pin,pin:column.effectivePin||"none",visible:!hiddenReason,hiddenReason,responsiveHidden:!!column.responsiveHidden,width:column.renderWidth||column.width})})};DataGrid.prototype.setColumnPinned=function(id,pin){const column=this.columns.find(item=>item.id===id);if(this.destroyed||!column||!COLUMN_PINS.includes(pin))throw new Error("CanopyX: pin needs a live grid, declared column and none, left or right.");if(this.cancelResize)this.cancelResize();const held=this.heldCell(),columnId=held&&this.cols[held.index]&&this.cols[held.index].id;column.pin=pin;this.buildHeader();this.syncColumnControls();this.render();this.restoreReportingFocus(held,columnId);this.stateChanged();this.emit("columnPin",{column:id,pin})};const PLACEMENT_WIDTHS=DataGrid.prototype.applyWidths,PLACEMENT_CELL=DataGrid.prototype.newCell;DataGrid.prototype.visibleColumns=function(){const requested=this.columns.filter(column=>!column.hidden),options=this.view.reporting||{};for(const column of this.columns)column.responsiveHidden=false;if(options.responsive&&this.grid&&this.grid.clientWidth&&this.host.clientWidth<=640&&(this.layoutMode||"fit")==="fit"){let total=requested.reduce((sum,column)=>sum+reportingWidth(this,column,column.width),0),remaining=requested.length;const candidates=requested.filter(column=>!reportingLayout(this,column).alwaysVisible).sort((a,b)=>
|
|
1285
1339
|
(reportingLayout(this,b).priority??MAX_PROTOCOL_INTEGER)-(reportingLayout(this,a).priority??MAX_PROTOCOL_INTEGER)||
|
|
1286
1340
|
requested.indexOf(b)- requested.indexOf(a));for(const column of candidates){if(total<=this.grid.clientWidth||remaining===1)break;column.responsiveHidden=true;total -=reportingWidth(this,column,column.width);remaining--}}
|
|
1287
1341
|
const visible=requested.filter(column=>!column.responsiveHidden&&!column.fitHidden);return["left","none","right"].flatMap(pin=>visible.filter(column=>column.pin===pin))};DataGrid.prototype.applyWidths=function(){this.fitLayout();const visible=this.visibleColumns();if(this.fitBuilding&&this.cols&&(visible.length!==this.cols.length||visible.some((column,index)=>column!==this.cols[index])))
|
|
@@ -1292,7 +1346,7 @@ const PLACEMENT_HEADER=DataGrid.prototype.buildHeader,PLACEMENT_MOVE=DataGrid.pr
|
|
|
1292
1346
|
DataGrid.prototype.revealInRow=function(cell,target,treeCell){PLACEMENT_REVEAL.call(this,cell,target,treeCell);if(cell.parentNode&&cell.parentNode._kind==="node")revealReportingCenter(this,target,cell.getAttribute("data-cx-pin"))};DataGrid.prototype.focusReportingHeader=function(control){PLACEMENT_FOCUS_HEADER.call(this,control);if(control)revealReportingCenter(this,control,control.closest(".cx-hcell").getAttribute("data-cx-pin"))};DataGrid.prototype.buildHeader=function(){if(!this.pinnedHeaderFocus){this.pinnedHeaderFocus=true;this.headRow.addEventListener("focusin",event=>{const cell=event.target.closest(".cx-hcell");if(cell&&this.headRow.contains(cell))revealReportingCenter(this,event.target,cell.getAttribute("data-cx-pin"))},{signal:this.lifetime.signal})}
|
|
1293
1347
|
const result=PLACEMENT_ACCESS_HEADER.call(this);this.syncPrintAvailability?.();return result};DataGrid.prototype.defaultExportColumns=function(){return reportingColumnOrder(this.columns.filter(column=>!column.hidden&&column.exportable!==false))};const PRESENTERS_VALIDATE=GRID_REPORTING.validate;GRID_REPORTING.validate=view=>{PRESENTERS_VALIDATE(view);const presenters=view.reporting.presenters;if(presenters===undefined)return;if(!presenters||typeof presenters!=="object"||Array.isArray(presenters))throw new Error("CanopyX: presenters are keyed by declared column ID.");for(const[id,options]of Object.entries(presenters)){const column=view.columns.find(item=>item&&item.id===id);if(!column)throw new Error("CanopyX: a presenter needs a declared column.");gridPresenter(options,column,view.columns)}};const FACET_API=GRID_REPORTING.api;GRID_REPORTING.api=(api,grid)=>{FACET_API?.(api,grid);api.getFacets=(column,options)=>grid.getFacets(column,options)};DataGrid.prototype.getFacets=async function(column,options={}){if(this.destroyed||this.pendingQuery||!this.acceptedQuery||!this.view.reporting?.facetColumns?.includes(column)||typeof this.source.facet!=="function")
|
|
1294
1348
|
throw new Error("CanopyX: facets need an accepted query, opted-in column and explicit source capability.");const search=options.search===undefined?"":options.search,limit=options.limit===undefined?100:options.limit,includeCurrentFilter=options.includeCurrentFilter===undefined?true:options.includeCurrentFilter;if(typeof search!=="string"||search.length>16384||!Number.isInteger(limit)||limit<1||limit>200||typeof includeCurrentFilter!=="boolean")
|
|
1295
|
-
throw new Error("CanopyX: invalid bounded facet request.");const controller=new AbortController(),dataSignal=this.dataLifetime.signal,epoch=this.queryEpoch;const abort=()=>controller.abort();dataSignal.addEventListener("abort",abort,{once:true});options.signal?.addEventListener("abort",abort,{once:true});if(options.signal?.aborted)abort();const request=gridFreeze({protocol:PROTOCOL,type:"gridFacetQuery",column,search,limit,includeCurrentFilter,query:this.acceptedQuery});try{const page=await this.runGridHook(signal=>this.source.facet(request,{signal}),controller.signal);gridOperationActive(this,controller.signal);if(epoch!==this.queryEpoch)throw new DOMException("Facet request was superseded.","AbortError");return reportingFacetPage(page,this,request)}finally{dataSignal.removeEventListener("abort",abort);options.signal?.removeEventListener("abort",abort);controller.abort()}};const VIEW_SETTINGS_LIMIT=1048576,VIEW_STATE_BASE={};for(const name of["prepareState","readStoredState","writeStoredState","saveView","removeSavedView","resetView"])VIEW_STATE_BASE[name]=DataGrid.prototype[name];function reportingStoredSettings(grid,input){const json=JSON.stringify(input);if(typeof json!=="string"||json.length>VIEW_SETTINGS_LIMIT)throw new Error("CanopyX: view preferences exceed the 1,048,576-character limit.");const value=JSON.parse(json);if(!value||Array.isArray(value)||Object.keys(value).some(key=>!["version","view","current","views","defaultView"].includes(key))||
|
|
1349
|
+
throw new Error("CanopyX: invalid bounded facet request.");const controller=new AbortController(),dataSignal=this.dataLifetime.signal,epoch=this.queryEpoch;const abort=()=>controller.abort();dataSignal.addEventListener("abort",abort,{once:true});options.signal?.addEventListener("abort",abort,{once:true});if(options.signal?.aborted)abort();const bucket=options.bucket===undefined?null:gridBucketDefinition(options.bucket,this.view.columns.find(item=>item.id===column));if(bucket)await loadPart(BUCKETS_PART);const request=gridFreeze({protocol:PROTOCOL,type:"gridFacetQuery",column,search,limit,includeCurrentFilter,query:this.acceptedQuery,...(bucket?{bucket}:{})});try{const page=await this.runGridHook(signal=>this.source.facet(request,{signal}),controller.signal);gridOperationActive(this,controller.signal);if(epoch!==this.queryEpoch)throw new DOMException("Facet request was superseded.","AbortError");return reportingFacetPage(page,this,request)}finally{dataSignal.removeEventListener("abort",abort);options.signal?.removeEventListener("abort",abort);controller.abort()}};const VIEW_SETTINGS_LIMIT=1048576,VIEW_STATE_BASE={};for(const name of["prepareState","readStoredState","writeStoredState","saveView","removeSavedView","resetView"])VIEW_STATE_BASE[name]=DataGrid.prototype[name];function reportingStoredSettings(grid,input){const json=JSON.stringify(input);if(typeof json!=="string"||json.length>VIEW_SETTINGS_LIMIT)throw new Error("CanopyX: view preferences exceed the 1,048,576-character limit.");const value=JSON.parse(json);if(!value||Array.isArray(value)||Object.keys(value).some(key=>!["version","view","current","views","defaultView"].includes(key))||
|
|
1296
1350
|
(value.version!==undefined||value.view!==undefined)&&(value.version!==1||value.view!==grid.view.id)||
|
|
1297
1351
|
!Array.isArray(value.views)||value.views.length>20)
|
|
1298
1352
|
throw new Error("CanopyX: invalid or incompatible view preferences.");const names=new Set(),views=[];for(const entry of value.views){if(!entry||Object.keys(entry).some(key=>!["name","state"].includes(key)))throw new Error("CanopyX: invalid saved view entry.");const name=grid.viewName(entry.name);if(names.has(name))throw new Error("CanopyX: repeated saved view name.");names.add(name);try{views.push({name,state:grid.readGridState(entry.state)})}
|
|
@@ -1311,7 +1365,7 @@ catch(error){this.viewSettingsIntent=intent;throw error}},removeSavedView(name){
|
|
|
1311
1365
|
return this.getQuery()},getShareState(){const state=this.getState();state.query=gridQuery(this.view,gridQueryStart({sort:state.query.sort,searchMode:state.query.searchMode}));state.selection=[];state.scrollTop=0;if(state.paging)state.paging.offset=0;return gridFreeze(state)}});const VIEW_STORE_INITIALIZE=DataGrid.prototype.initializeState,VIEW_STORE_QUERY=DataGrid.prototype.setQuery,VIEW_STORE_SEARCH=DataGrid.prototype.buildGridSearch,VIEW_STORE_STATE=DataGrid.prototype.setState,VIEW_STORE_PRESENTATION=DataGrid.prototype.applyStatePresentation,VIEW_STORE_API=GRID_REPORTING.api;GRID_REPORTING.api=(api,grid)=>{VIEW_STORE_API?.(api,grid);for(const name of["getViewSettings","getDefaultView","setDefaultView","renameSavedView","getShareState","flushViewSettings"])
|
|
1312
1366
|
api[name]=grid[name].bind(grid)};GRID_REPORTING.search=(grid,update)=>{grid.scheduleGridSearch=delay=>{clearTimeout(grid.searchTimer);grid.searchTimer=setTimeout(update,delay)}};for(const name of["setColumnWidth","setColumnVisible","setColumnOrder","setColumnPinned","setTimeZone","setSelection"]){const original=DataGrid.prototype[name];DataGrid.prototype[name]=function(...args){const result=original.apply(this,args);this.noteViewPreferenceIntent();return result}}
|
|
1313
1367
|
Object.assign(DataGrid.prototype,{noteViewPreferenceIntent(){this.viewPreferenceIntent++},buildGridSearch(){VIEW_STORE_SEARCH.call(this);for(const event of["input","compositionstart","compositionend"])
|
|
1314
|
-
this.searchInput.addEventListener(event,()=>{this.noteViewPreferenceIntent();this.searchDeadline=performance.now()+ this.searchDelayMs},{signal:this.lifetime.signal})},setState(input){this.restorePreferenceIntent=this.viewPreferenceIntent;return VIEW_STORE_STATE.call(this,input)},applyStatePresentation(state){if(this.restorePreferenceIntent===this.viewPreferenceIntent)return VIEW_STORE_PRESENTATION.call(this,state)},setQuery(input,paging){const pending=VIEW_STORE_QUERY.call(this,input,paging);this.viewQueryPromise=pending;return pending},async initializeState(){if(!this.opts.viewStore)return VIEW_STORE_INITIALIZE.call(this);this.loadingViewSettings=true;this.setViewPreferenceError(this.labels.loading);const epoch=this.queryEpoch,intent=this.viewSettingsIntent,preference=this.viewPreferenceIntent;try{const input=await this.runGridHook(signal=>this.opts.viewStore.load({key:this.storageKey,view:this.view.id,signal}));gridOperationActive(this);this.setViewPreferenceError("");if(input!==null&&input!==undefined&&intent===this.viewSettingsIntent){const settings=reportingStoredSettings(this,input);this.savedViews=new Map(settings.views.map(entry=>[entry.name,entry.state]));this.defaultSavedView=settings.defaultView;if(epoch===this.queryEpoch&&preference===this.viewPreferenceIntent)
|
|
1368
|
+
this.searchInput.addEventListener(event,()=>{this.noteViewPreferenceIntent();this.searchDeadline=performance.now()+ this.searchDelayMs},{signal:this.lifetime.signal})},setState(input){this.restorePreferenceIntent=this.viewPreferenceIntent;return VIEW_STORE_STATE.call(this,input)},applyStatePresentation(state){if(this.restorePreferenceIntent===this.viewPreferenceIntent)return VIEW_STORE_PRESENTATION.call(this,state)},setQuery(input,paging){const pending=VIEW_STORE_QUERY.call(this,input,paging);this.viewQueryPromise=pending;return pending},async initializeState(){if(gridUsesCalculations(this.view)&&!this.calculationPrepared)await this.prepareCalculations();if(!this.opts.viewStore)return VIEW_STORE_INITIALIZE.call(this);this.loadingViewSettings=true;this.setViewPreferenceError(this.labels.loading);const epoch=this.queryEpoch,intent=this.viewSettingsIntent,preference=this.viewPreferenceIntent;try{const input=await this.runGridHook(signal=>this.opts.viewStore.load({key:this.storageKey,view:this.view.id,signal}));gridOperationActive(this);this.setViewPreferenceError("");if(input!==null&&input!==undefined&&intent===this.viewSettingsIntent){const settings=reportingStoredSettings(this,input);this.savedViews=new Map(settings.views.map(entry=>[entry.name,entry.state]));this.defaultSavedView=settings.defaultView;if(epoch===this.queryEpoch&&preference===this.viewPreferenceIntent)
|
|
1315
1369
|
this.initialState=this.defaultSavedView?this.savedViews.get(this.defaultSavedView):settings.current;this.syncSavedViews();this.setViewPreferenceError(this.viewPreferenceError||"")}}catch(error){if(this.destroyed)throw error;this.viewStoreFaulted=true;this.setViewPreferenceError(error.message)}finally{this.loadingViewSettings=false}
|
|
1316
1370
|
let pending=this.viewQueryPromise;if(epoch===this.queryEpoch){const deadline=this.searchTimer==null?null:this.searchDeadline,text=this.searchInput?.value;pending=VIEW_STORE_INITIALIZE.call(this);if(deadline!==null){this.searchInput.value=text;this.scheduleGridSearch(Math.max(0,deadline-performance.now()))}}
|
|
1317
1371
|
while(pending){try{await pending;if(pending===this.viewQueryPromise)return this.getQuery()}
|
|
@@ -1348,9 +1402,9 @@ const excluded=new Set(request.querySelection.excludedIds);const filtered={...re
|
|
|
1348
1402
|
if(!excluded.delete(row.id))yield row;if(excluded.size)throw new Error("CanopyX: excluded records are absent from the accepted query revision.")};DataGrid.prototype.exportText=async function(exported,maxRows=50000,delimiter,signal){const selection=exported.request.querySelection;if(selection&&selection.matched!==null&&Number.isInteger(maxRows)&&maxRows>=1&&maxRows<=1000000&&
|
|
1349
1403
|
selection.matched-selection.excludedIds.length>maxRows)
|
|
1350
1404
|
throw new Error("CanopyX: export exceeds the client row limit; use a host export hook.");return SELECTION_EXPORT_TEXT.call(this,exported,maxRows,delimiter,signal)};DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridSelectionQueryCount:"{count} matching records selected; {excluded} excluded",gridSelectRecord:"Select record {id}",gridSelection:"Selection",gridSelectPage:"Select page",gridSelectMatching:"Select all matching"}));const SELECTION_CONTROLS_BASE={};for(const name of["buildGridControls","fillRow","refreshRowState","onClick","onKey","setQuery","closeMenusOutside"])
|
|
1351
|
-
SELECTION_CONTROLS_BASE[name]=DataGrid.prototype[name];Object.assign(DataGrid.prototype,{closeMenusOutside(node){SELECTION_CONTROLS_BASE.closeMenusOutside.call(this,node);const menu=this.headRow?.querySelector(".cx-header-selection");if(menu?.open&&!menu.contains(node))menu.open=false},buildGridControls(){SELECTION_CONTROLS_BASE.buildGridControls.call(this);if(!this.view.reporting?.selectionControls)return;const controls=h("div","cx-grid-selection-controls"),status=this.selectionStatus=h("span","cx-grid-selection-status");controls.setAttribute("role","group");controls.setAttribute("aria-label",this.labels.gridSelection);status.setAttribute("role","status");const page=this.multi&&this.paged?gridButton(this.labels.gridSelectPage,()=>this.runReportingSelection(()=>this.selectPage())):null;const all=this.multi?gridButton(this.labels.gridSelectMatching,()=>this.runReportingSelection(()=>this.selectAllMatching())):null;const clear=gridButton(this.labels.gridClearSelection,()=>this.setSelection([]));this.selectionButtons=[page,all,clear].filter(Boolean);controls.append(...this.selectionButtons,status);this.selectionControls=controls;this.syncSelectionControls()},syncSelectionControls(){if(!this.selectionStatus)return;for(const button of this.selectionButtons)button.disabled=!!this.selectionControlBusy||!this.acceptedQuery||!!this.pendingQuery;for(const[row,element]of this.rendered)if(!row.status){const check=element.querySelector(".cx-grid-row-select");if(check){check.checked=this.reportingRowSelected(row);check.disabled=!!this.pendingQuery}}
|
|
1405
|
+
SELECTION_CONTROLS_BASE[name]=DataGrid.prototype[name];Object.assign(DataGrid.prototype,{closeMenusOutside(node){SELECTION_CONTROLS_BASE.closeMenusOutside.call(this,node);const menu=this.headRow?.querySelector(".cx-header-selection");if(menu?.open&&!menu.contains(node))menu.open=false},buildGridControls(){SELECTION_CONTROLS_BASE.buildGridControls.call(this);if(!this.view.reporting?.selectionControls)return;const controls=h("div","cx-grid-selection-controls"),status=this.selectionStatus=h("span","cx-grid-selection-status");controls.setAttribute("role","group");controls.setAttribute("aria-label",this.labels.gridSelection);status.setAttribute("role","status");const page=this.multi&&this.paged?gridButton(this.labels.gridSelectPage,()=>this.runReportingSelection(()=>this.selectPage())):null;const all=this.multi?gridButton(this.labels.gridSelectMatching,()=>this.runReportingSelection(()=>this.selectAllMatching())):null;const clear=this.selectionClear=gridButton(this.labels.gridClearSelection,()=>this.setSelection([]));this.selectionButtons=[page,all,clear].filter(Boolean);controls.append(...this.selectionButtons,status);this.selectionControls=controls;this.syncSelectionControls()},syncSelectionControls(){if(!this.selectionStatus)return;for(const button of this.selectionButtons)button.disabled=!!this.selectionControlBusy||!this.acceptedQuery||!!this.pendingQuery;this.selectionClear.disabled||=this.querySelection?this.querySelection.matched===this.selectionExcluded.size:!this.selected.size;for(const[row,element]of this.rendered)if(!row.status){const check=element.querySelector(".cx-grid-row-select");if(check){check.checked=this.reportingRowSelected(row);check.disabled=!!this.pendingQuery}}
|
|
1352
1406
|
const query=this.querySelection,format=numberFormat(this.lang);setText(this.selectionStatus,query?fill(query.matched===null?this.labels.gridSelectionQueryUnknown:this.labels.gridSelectionQueryCount,{count:query.matched===null?"":format.format(Math.max(0,query.matched-this.selectionExcluded.size)),excluded:format.format(this.selectionExcluded.size)}):
|
|
1353
|
-
fill(this.labels.gridSelectionCount,{count:format.format(this.selected.size)}));const header=this.headRow?.querySelector(".cx-header-check"),count=query?query.matched===null?null:query.matched-this.selectionExcluded.size:this.selected.size;if(header){header.dataset.cxChecked=count===0?"false":count===this.snapshot?.matched?"true":"mixed";header.setAttribute("aria-label",this.labels.gridSelection+" · "+this.selectionStatus.textContent)}},buildHeaderSelection(){this.headerSelectionFlyout?.dispose();if(!this.selectionControls||!this.headCells[0])return;const menu=h("details","cx-columns cx-header-selection"),summary=h("summary"),panel=h("div","cx-columns-list cx-grid-panel");summary.setAttribute("aria-label",this.labels.gridSelection);summary.classList.add("cx-header-check");summary.setAttribute("aria-haspopup","menu");
|
|
1407
|
+
fill(this.labels.gridSelectionCount,{count:format.format(this.selected.size)}));const header=this.headRow?.querySelector(".cx-header-check"),count=query?query.matched===null?null:query.matched-this.selectionExcluded.size:this.selected.size;if(header){header.dataset.cxChecked=count===0?"false":count===this.snapshot?.matched?"true":"mixed";header.setAttribute("aria-label",this.labels.gridSelection+" · "+this.selectionStatus.textContent)}},buildHeaderSelection(){this.headerSelectionFlyout?.dispose();if(!this.selectionControls||!this.headCells[0])return;const menu=h("details","cx-columns cx-header-selection"),summary=h("summary"),panel=h("div","cx-columns-list cx-grid-panel");summary.setAttribute("aria-label",this.labels.gridSelection);summary.classList.add("cx-header-check");summary.setAttribute("aria-haspopup","menu");panel.appendChild(this.selectionControls);menu.append(summary,panel);this.headCells[0].prepend(menu);const flyout=this.headerSelectionFlyout=watchFlyout(menu,panel,()=>menu.open);panel.popover="manual";menu.addEventListener("toggle",()=>{if(menu.open){announceMenu(menu);if(panel.isConnected)panel.showPopover?.();flyout.position()}else panel.hidePopover?.()});menu.addEventListener("keydown",event=>{if(event.key==="Escape"){menu.open=false;summary.focus();event.preventDefault()}});this.syncSelectionControls()},runReportingSelection(action){this.selectionControlBusy=true;this.syncSelectionControls();Promise.resolve().then(action).catch(error=>{if(!this.destroyed&&error.name!=="AbortError"){this.selectionControlError=error.message;this.emit("error",{error})}}).finally(()=>{this.selectionControlBusy=false;if(!this.destroyed){this.syncSelectionControls();if(this.selectionControlError){setText(this.selectionStatus,this.selectionControlError);this.selectionControlError=null}}})},setQuery(input,paging){const pending=SELECTION_CONTROLS_BASE.setQuery.call(this,input,paging);this.syncSelectionControls();return pending},refreshRowState(element,row){SELECTION_CONTROLS_BASE.refreshRowState.call(this,element,row);if(this.view.reporting?.selectionControls&&!row.status)element.setAttribute("aria-selected",String(this.reportingRowSelected(row)))},fillRow(element,row){SELECTION_CONTROLS_BASE.fillRow.call(this,element,row);if(!this.view.reporting?.selectionControls||row.status)return;const cell=element._cells[0].cell;let check=cell.querySelector(".cx-grid-row-select");if(!check){check=h("input","cx-grid-row-select");check.type="checkbox";check.tabIndex=-1;cell.insertBefore(check,cell.firstChild);check.addEventListener("click",event=>{if(!event.shiftKey)return;event.preventDefault();const current=element._w;this.reportingSelectionIntent(current,!this.reportingRowSelected(current),true,event.ctrlKey||event.metaKey)});check.addEventListener("change",()=>{this.reportingSelectionIntent(element._w,check.checked,false,false)});check.addEventListener("keydown",event=>{if(event.key===" "&&event.shiftKey){event.preventDefault();event.stopPropagation();const current=element._w;this.reportingSelectionIntent(current,!this.reportingRowSelected(current),true,event.ctrlKey||event.metaKey)}})}
|
|
1354
1408
|
check.checked=this.reportingRowSelected(row);check.disabled=!!this.pendingQuery;check.setAttribute("aria-label",fill(this.labels.gridSelectRecord,{id:row.key}))},reportingSelectionIntent(row,selected,range,additive){this.runReportingSelection(()=>range&&this.multi&&this.selectionAnchor!=null?
|
|
1355
1409
|
this.selectRange(this.selectionAnchor,row._ri,{selected,additive}):this.setReportingRowSelected(row,selected))},onClick(event){const element=event.target.closest(".cx-row"),row=element?._w;if(this.view.reporting?.selectionControls&&row&&!row.status&&!event.defaultPrevented&&
|
|
1356
1410
|
!event.target.closest("a,button,input,select,textarea,[contenteditable]")&&(event.shiftKey||event.ctrlKey||event.metaKey)){this.setFocus(row,true);this.reportingSelectionIntent(row,event.shiftKey||!this.reportingRowSelected(row),event.shiftKey,event.ctrlKey||event.metaKey);return}
|
|
@@ -1359,12 +1413,12 @@ catch(error){if(!this.view.reporting?.selectionControls)throw error;this.runRepo
|
|
|
1359
1413
|
if(this.view.reporting?.selectionControls&&row&&!row.status&&!event.defaultPrevented&&
|
|
1360
1414
|
!event.target.closest("a,button,input,select,textarea,[contenteditable]"))this.selectionAnchor=row._ri},onKey(event){const element=event.target.closest(".cx-row"),row=element?._w;if(this.view.reporting?.selectionControls&&row&&!row.status&&event.key===" "&&!event.defaultPrevented&&
|
|
1361
1415
|
!event.isComposing&&!event.altKey&&!event.target.closest("a,button,input,select,textarea,[contenteditable]")){event.preventDefault();this.reportingSelectionIntent(row,!this.reportingRowSelected(row),event.shiftKey,event.ctrlKey||event.metaKey);return}
|
|
1362
|
-
return SELECTION_CONTROLS_BASE.onKey.call(this,event)}});DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridGroupHeading:"{column}: {value} ({count} records)",gridGroupMissing:"Missing value",gridGroupExpand:"Expand group",gridGroupCollapse:"Collapse group",gridSummary:"
|
|
1416
|
+
return SELECTION_CONTROLS_BASE.onKey.call(this,event)}});DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridGroupHeading:"{column}: {value} ({count} records)",gridGroupMissing:"Missing value",gridGroupExpand:"Expand group",gridGroupCollapse:"Collapse group",gridSummary:"Grand total",gridGroupOrder:"Group order is fixed by this report",gridGroupBy:"Group by this column",gridUngroup:"Stop grouping by this column",gridGroupDrop:"Drag a column header here to group rows",gridExpandAll:"Expand all",gridCollapseAll:"Collapse all",gridExpandLevel:"Expand level {level}",gridCollapseLevel:"Collapse level {level}",gridGroupMove:"Alt+Left and Alt+Right move this level",gridGroupSortBy:"Sort groups by",gridGroupSortValue:"by value",gridGroupSortAggregate:"by {aggregate}",gridSelectGroup:"Select the records of {group}",gridAggregateCount:"count {value}",gridAggregateSum:"sum {value}",gridAggregateAverage:"avg {value}",gridAggregateMinimum:"min {value}",gridAggregateMaximum:"max {value}",gridAggregateDistinct:"distinct {value}",gridShowDetail:"Show details",gridHideDetail:"Hide details",gridDetailOf:"Details of {record}",gridDetailFailed:"Details could not load: {error}"}));const GROUP_LEVELS=4,GROUP_OPERATIONS=["count","sum","average","minimum","maximum","distinctCount"];const GROUP_NUMERIC=["sum","average","minimum","maximum"],GRID_ROWS={base:{}};const reportingDynamicGroups=view=>view.reporting?.groupBy!==undefined||!!view.reporting?.groupable;function reportingSummaryEnabled(view){return!!view.reporting?.grouping||reportingDynamicGroups(view)||!!view.reporting?.aggregates?.length}
|
|
1363
1417
|
const reportingRowsUsed=grid=>reportingSummaryEnabled(grid.view)||!!grid.view.reporting?.detail||!!grid.opts.detail;function reportingGroupKey(value){return JSON.stringify([typeof value,value])}
|
|
1364
1418
|
function reportingGroupLevels(view,input,columns=view.columns){if(!Array.isArray(input)||input.length>GROUP_LEVELS)throw new Error("CanopyX: groupBy lists at most "+GROUP_LEVELS+" levels.");const seen=new Set(),aggregates=view.reporting.aggregates||[];return input.map(level=>{const column=level&&columns.find(item=>item.id===level.column);if(!column||!column.sortable||seen.has(level.column)||!["asc","desc"].includes(level.direction)||
|
|
1365
|
-
Object.keys(level).some(key=>!["column","direction","aggregate"].includes(key))||
|
|
1419
|
+
Object.keys(level).some(key=>!["column","direction","aggregate","bucket"].includes(key))||
|
|
1366
1420
|
level.aggregate!==undefined&&!aggregates.some(aggregate=>aggregate.id===level.aggregate))
|
|
1367
|
-
throw new Error("CanopyX: invalid groupBy level.");seen.add(level.column);return gridFreeze({column:level.column,direction:level.direction,...(level.aggregate===undefined?{}:{aggregate:level.aggregate})})})}
|
|
1421
|
+
throw new Error("CanopyX: invalid groupBy level.");seen.add(level.column);return gridFreeze({column:level.column,direction:level.direction,...(level.aggregate===undefined?{}:{aggregate:level.aggregate}),...(level.bucket===undefined?{}:{bucket:gridBucketDefinition(level.bucket,column)})})})}
|
|
1368
1422
|
const GROUPING_VALIDATE=GRID_REPORTING.validate;GRID_REPORTING.validate=view=>{GROUPING_VALIDATE(view);const options=view.reporting,grouping=options.grouping,aggregates=options.aggregates||[],ids=new Set();if(reportingSummaryEnabled(view)&&view.source.navigation==="cursor")throw new Error("CanopyX: summaries require offset navigation.");if(grouping!==undefined&&(!grouping||typeof grouping!=="object"||Array.isArray(grouping)||
|
|
1369
1423
|
!["asc","desc"].includes(grouping.direction)||
|
|
1370
1424
|
!view.columns.some(column=>column.id===grouping.column&&column.sortable)))throw new Error("CanopyX: invalid grouping column or order.");if(grouping?.headerTemplate!==undefined&&(typeof grouping.headerTemplate!=="string"||!grouping.headerTemplate.trim()||grouping.headerTemplate.length>512))
|
|
@@ -1383,55 +1437,62 @@ detail.label!==undefined&&(typeof detail.label!=="string"||!detail.label.trim()|
|
|
|
1383
1437
|
throw new Error("CanopyX: invalid detail rows definition.")};const GROUPING_QUERY=gridQuery,GROUPING_SHAPE=gridResultShape;gridQuery=(view,input,strict)=>{const grouping=view.reporting?.grouping;if(grouping){const query=GROUPING_QUERY(view,input,strict);return gridFreeze({...query,sort:[{column:grouping.column,direction:grouping.direction},...query.sort.filter(sort=>sort.column!==grouping.column)]})}
|
|
1384
1438
|
if(!reportingDynamicGroups(view)||!input||typeof input!=="object")return GROUPING_QUERY(view,input,strict);const{groupBy,...rest}=input,query=GROUPING_QUERY(view,rest,strict);const levels=reportingGroupLevels(view,groupBy===undefined?view.reporting.groupBy||[]:groupBy).map(level=>{const sort=!level.aggregate&&query.sort.find(item=>item.column===level.column);return sort&&sort.direction!==level.direction?gridFreeze({...level,direction:sort.direction}):level});if(!levels.length)return gridFreeze({...query,groupBy:levels});const lead=levels.map(level=>({column:level.column,direction:level.aggregate?"asc":level.direction}));return gridFreeze({...query,groupBy:levels,sort:[...lead,...query.sort.filter(sort=>!levels.some(level=>level.column===sort.column))]})};gridResultShape=query=>query.groupBy?GROUPING_SHAPE(query)+ JSON.stringify(query.groupBy):GROUPING_SHAPE(query);function reportingGroupCompare(compare){const plain=(row,column)=>{const cell=row.cells[column.id];return isCellObject(cell)&&cell.sort!=null?{cells:{[column.id]:{...cell,sort:null}}}:row};return(a,b,column,direction)=>{const compared=compare(plain(a,column),plain(b,column),column,direction);if(compared)return compared;const left=gridScalar(a.cells[column.id],column),right=gridScalar(b.cells[column.id],column);const result=gridOrder(typeof left,typeof right)||gridOrder(String(left),String(right));return direction==="desc"?-result:result}}
|
|
1385
1439
|
const GROUPING_COMPARE=GRID_REPORTING.compare;GRID_REPORTING.compare=view=>{const compare=GROUPING_COMPARE(view),grouping=view.reporting?.grouping;if(!grouping)return compare;const group=reportingGroupCompare(compare);return(a,b,column,direction)=>column.id===grouping.column?group(a,b,column,direction):compare(a,b,column,direction)};const GROUPING_INLINE=inlineGridSource;inlineGridSource=view=>{const base=GROUPING_INLINE(view),plain=base.query;if(!reportingDynamicGroups(view))return base;let shape=null,ordered=null;base.query=function(query,options){if(!query.groupBy)return plain.call(base,query,options);if(options.signal.aborted)throw new DOMException("Grid cancelled.","AbortError");if(gridResultShape(query)!==shape){ordered=GRID_ROWS.order(view,query);shape=gridResultShape(query)}
|
|
1386
|
-
return{protocol:PROTOCOL,type:"gridPage",view:view.id,revision:
|
|
1440
|
+
return{protocol:PROTOCOL,type:"gridPage",view:view.id,revision:gridInlineRevision(view),offset:query.offset,total:view.rows.length,matched:ordered.length,items:ordered.slice(query.offset,query.offset+query.limit)}};return base};const DETAIL_ROWS=definePart(DataGrid,"detail-rows","detail rows",()=>({DataGrid,GRID_ROWS,GRID_REPORTING,h,setText,fill,gridButton,gridFreeze,cellText,formatting,labelsFor,requireIdentifier,GridRowOffsets,TreeGrid,createDataGrid,createTreeGrid,disposeRowHostCells,OWN_ARROWS,hostHtml}));const GROUPING=definePart(DataGrid,"grouping","row grouping",()=>({DataGrid,GRID_ROWS,GRID_REPORTING,GRID_OPTIONS,GRID_OPTION_GRIDS,h,setText,fill,numberFormat,formatValue,cellValue,cellText,isCellObject,gridScalar,gridQuery,gridFreeze,gridOrder,gridIdOrder,gridQueryRows,gridButton,gridDecorateButton,gridCellRenderer,gridPresenterTone,GRID_TONE_RANK,validateGridRows,validateGridPage,checkDocument,requireIdentifier,validStructuralCount,PROTOCOL,gridOperationActive,reportingGroupKey,reportingGroupCompare,reportingSummaryEnabled,reportingDynamicGroups,GROUP_OPERATIONS,reportingAggregateParts,disposeRowHostCells,quietControls,TreeGrid,GRID_BUCKETS,gridBucketDefinition}));GRID_ROWS.base.requestPage=DataGrid.prototype.requestPage;Object.assign(DataGrid.prototype,{loadRows(){return this.rowsLoad||=loadPart(reportingSummaryEnabled(this.view)?GROUPING:DETAIL_ROWS).then(()=>{if(!this.destroyed)this.attachRows()},error=>{this.rowsLoad=null;throw error})},async requestPage(query,signal){if(!reportingRowsUsed(this))return GRID_ROWS.base.requestPage.call(this,query,signal);if(query.groupBy?.some(level=>level.bucket)||gridUsesBuckets(this.view))await loadPart(BUCKETS_PART);await this.loadRows();return(GRID_ROWS.requestPage||GRID_ROWS.base.requestPage).call(this,query,signal)},setGroupBy(groupBy){if(!reportingDynamicGroups(this.view))throw new Error("CanopyX: this report has no runtime grouping.");const query=this.queryForChange(true),levels=reportingGroupLevels(this.view,groupBy);const removed=(query.groupBy||[]).filter(level=>!levels.some(next=>next.column===level.column)).map(level=>level.column);return this.setQuery(gridQueryStart(query,{groupBy:levels,sort:(query.sort||[]).filter(sort=>!removed.includes(sort.column)&&!levels.some(level=>level.column===sort.column))}))},getGroupBy(){const grouping=this.view.reporting?.grouping;return gridFreeze(grouping?[{column:grouping.column,direction:grouping.direction}]:
|
|
1387
1441
|
JSON.parse(JSON.stringify((this.acceptedQuery||gridQuery(this.view,this.view.query||{})).groupBy||[])))}});const GROUPING_API=GRID_REPORTING.api;GRID_REPORTING.api=(api,grid)=>{GROUPING_API?.(api,grid);const later=name=>(...args)=>grid.loadRows().then(()=>grid[name](...args));if(reportingSummaryEnabled(grid.view)){api.getSummary=()=>grid.summary||null;api.getGroupBy=()=>grid.getGroupBy();if(reportingDynamicGroups(grid.view))api.setGroupBy=groupBy=>grid.setGroupBy(groupBy)}
|
|
1388
1442
|
if(grid.view.reporting?.grouping||reportingDynamicGroups(grid.view)){api.setGroupCollapsed=(key,collapsed)=>{if(!grid.setGroupCollapsed)throw new Error("CanopyX: choose an accepted group and a boolean collapse intent.");return grid.setGroupCollapsed(key,collapsed)};api.getCollapsedGroups=()=>[...(grid.collapsedGroups||[])];api.expandAllGroups=later("expandAllGroups");api.collapseAllGroups=later("collapseAllGroups");api.setGroupLevelCollapsed=later("setGroupLevelCollapsed")}
|
|
1389
1443
|
if(grid.view.reporting?.detail||grid.opts.detail){for(const name of["expandDetail","collapseDetail","toggleDetail"])api[name]=later(name);api.getExpandedDetails=()=>[...(grid.detailRecords?.keys()||[])]}};const GROUP_STATE_BASE={};for(const name of["getState","readGridState","applyStatePresentation","getShareState"])GROUP_STATE_BASE[name]=DataGrid.prototype[name];const reportingGrouped=view=>!!view.reporting?.grouping||reportingDynamicGroups(view);Object.assign(DataGrid.prototype,{getState(){const state=GROUP_STATE_BASE.getState.call(this);if(reportingGrouped(this.view))state.collapsedGroups=[...(this.collapsedGroups||[])];return state},readGridState(input){const query=input?.query,aggregates=this.view.reporting?.aggregates||[],levels=query&&reportingDynamicGroups(this.view)&&Array.isArray(query.groupBy)?
|
|
1390
1444
|
query.groupBy.filter(level=>level&&this.columns.some(column=>column.id===level.column&&column.sortable)).slice(0,GROUP_LEVELS)
|
|
1391
1445
|
.map(level=>level.aggregate===undefined||aggregates.some(item=>item.id===level.aggregate)?level:(({aggregate,...rest})=>rest)(level)):undefined;const state=GROUP_STATE_BASE.readGridState.call(this,levels?{...input,query:{...query,groupBy:levels}}:input);const groups=input.collapsedGroups===undefined?[]:input.collapsedGroups;if(!Array.isArray(groups)||groups.length>10000||groups.some(key=>typeof key!=="string"||!key.length||key.length>4096))
|
|
1392
1446
|
throw new Error("CanopyX: invalid bounded group collapse state.");groups.forEach(key=>requireIdentifier(key,"group key"));if(reportingGrouped(this.view))state.collapsedGroups=[...new Set(groups)];return state},applyStatePresentation(state){if(reportingGrouped(this.view)){this.collapsedGroups=new Set(state.collapsedGroups||[]);this.groupCollapseVersion=(this.groupCollapseVersion||0)+ 1}
|
|
1393
|
-
return GROUP_STATE_BASE.applyStatePresentation.call(this,state)},getShareState(){const{collapsedGroups,...state}=GROUP_STATE_BASE.getShareState.call(this),current=this.getQuery()||this.defaultState.query;if(current.groupBy)state.query=gridQuery(this.view,gridQueryStart({sort:current.sort,searchMode:current.searchMode,groupBy:current.groupBy}));return gridFreeze(state)}});const AGGREGATE_LABELS={count:"gridAggregateCount",sum:"gridAggregateSum",average:"gridAggregateAverage",minimum:"gridAggregateMinimum",maximum:"gridAggregateMaximum",distinctCount:"gridAggregateDistinct"};const reportingCounting=operation=>operation==="count"||operation==="distinctCount";
|
|
1394
|
-
{
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
const
|
|
1403
|
-
GRID_REPORTING.captureExport=(grid,request)=>GRID_EXPORT_PRESENTATION.set(request,JSON.parse(JSON.stringify({rules:grid.view.highlightRules||[],columns:grid.view.columns,labels:grid.labels,locale:grid.lang})));GRID_REPORTING.exportColumn=function(grid,column){const policy=grid.columnLayouts?.get(column.id),presenter=column.presenter||grid.view.reporting?.presenters?.[column.id];const minimum=column.minWidth??policy?.minimum??40,maximum=column.maxWidth??policy?.maximum;return{id:column.id,title:column.title,kind:column.kind,width:{unit:"css-px",preferred:column.width,minimum,...(maximum===undefined?{}:{maximum})},wrap:!!column.wrap,alignment:column.kind==="number"?"right":"left",...(column.format?{format:{...column.format}}:{}),...(presenter?{presenter:JSON.parse(JSON.stringify(presenter))}:{}),...JSON.parse(JSON.stringify({valueLabels:column.valueLabels,valueTones:column.valueTones,numericTones:column.numericTones})),...(column.render&&column.hostRenderer!==false?{diagnostics:["custom-renderer"]}:{})}};GRID_REPORTING.exportRecord=function(grid,request,row){const capture=GRID_EXPORT_PRESENTATION.get(request),matches=new Map(),cells=Object.create(null);for(const rule of capture.rules){const key=rule.target==="cell"?rule.column:null;if(!matches.has(key)&&gridMatches(row,rule,capture.columns.find(column=>column.id===rule.column)))matches.set(key,rule)}
|
|
1404
|
-
const fmt=formatting(request.locale||capture.locale,request.timeZone==="local",capture.labels);for(const column of request.columns){const cell=row.cells[column.id],object=isCellObject(cell),value=cellValue(cell)??null,presenter=column.presenter;let text=cellText(cell,column,fmt);const missing=presenter&&(value===null&&(!object||cell.text===undefined)||text==="");const diagnostics=[...(column.diagnostics||[])];if(object&&cell.className)diagnostics.push("custom-class");if(object&&cell.badges?.length)diagnostics.push("badges");if(object&&cell.series)diagnostics.push("history-series");if(missing)text="—";if(!missing&&presenter?.kind==="boolean"&&typeof value==="boolean")text=value?capture.labels.yes:capture.labels.no;if(!missing&&presenter?.kind==="identifier"){const secondary=presenter.secondaryColumn?cellText(row.cells[presenter.secondaryColumn],capture.columns.find(item=>item.id===presenter.secondaryColumn),fmt):object&&cell.secondary;if(secondary)text +="\n"+secondary}
|
|
1447
|
+
return GROUP_STATE_BASE.applyStatePresentation.call(this,state)},getShareState(){const{collapsedGroups,...state}=GROUP_STATE_BASE.getShareState.call(this),current=this.getQuery()||this.defaultState.query;if(current.groupBy)state.query=gridQuery(this.view,gridQueryStart({sort:current.sort,searchMode:current.searchMode,groupBy:current.groupBy}));return gridFreeze(state)}});const AGGREGATE_LABELS={count:"gridAggregateCount",sum:"gridAggregateSum",average:"gridAggregateAverage",minimum:"gridAggregateMinimum",maximum:"gridAggregateMaximum",distinctCount:"gridAggregateDistinct"};const reportingCounting=operation=>operation==="count"||operation==="distinctCount";function reportingAggregateName(grid,aggregate){const template=grid.labels[AGGREGATE_LABELS[aggregate.operation]]||grid.groupAggregates?.get(aggregate.operation)?.label||aggregate.operation;const operation=template.replace("{value}","").trim()||aggregate.operation;return aggregate.label+" · "+operation.charAt(0).toLocaleUpperCase(grid.lang)+ operation.slice(1)}
|
|
1448
|
+
function reportingValueText(grid,aggregate,value,mode="value"){if(value===null||value===undefined)return "—";const column=grid.view.columns.find(column=>column.id===aggregate.column);if(GRID_SHOW_AS.percent?.(mode))return new Intl.NumberFormat(grid.lang,{style:"percent",minimumFractionDigits:2,maximumFractionDigits:2}).format(value).replace(/[\s\u00a0\u202f]+%/g, "%");
|
|
1449
|
+
const format=aggregate.format||(!reportingCounting(aggregate.operation)&&column?.format);return mode==="rank"?numberText(value,grid.lang,{minimumFractionDigits:0,maximumFractionDigits:0}):
|
|
1450
|
+
format?numberText(value,grid.lang,format):numberText(value,grid.lang,{minimumFractionDigits:reportingCounting(aggregate.operation)?0:2,maximumFractionDigits:reportingCounting(aggregate.operation)?0:2})}
|
|
1451
|
+
function reportingAggregateParts(grid,column,aggregates,values){const text=[],titles=[];for(const aggregate of aggregates){const mode=showAsMode(grid,aggregate),display=mode==="value"?null:GRID_SHOW_AS.entry?.(grid,values,aggregate);const value=display?display.value:values[aggregate.id];if((value===null||value===undefined)&&mode==="value")continue;const shown=reportingValueText(grid,aggregate,value,mode),caption=showAsCaption(grid,mode);text.push(mode==="value"?fill(grid.labels[AGGREGATE_LABELS[aggregate.operation]]||grid.groupAggregates?.get(aggregate.operation)?.label||aggregate.operation+" {value}",{value:shown}):shown+" "+caption.replace(/^% /, ""));
|
|
1452
|
+
titles.push(reportingAggregateName(grid,aggregate)+ ": "+shown +(caption?" ("+caption+")":""))}
|
|
1453
|
+
return{text:text.join(" · "),title:titles.join(" · "),caption:""}}
|
|
1454
|
+
const GRID_EXPORT_PRESENTATION=new WeakMap();function gridCopyExportPresentation(from,to){GRID_REPORTING.copyExportFormatting?.(from,to);const capture=GRID_EXPORT_PRESENTATION.get(from);if(capture)GRID_EXPORT_PRESENTATION.set(to,capture)}
|
|
1455
|
+
GRID_REPORTING.captureExport=(grid,request)=>GRID_EXPORT_PRESENTATION.set(request,JSON.parse(JSON.stringify({rules:grid.view.highlightRules||[],columns:grid.view.columns,labels:grid.labels,locale:grid.lang})));GRID_REPORTING.captureCardText=(request,columns)=>{GRID_EXPORT_PRESENTATION.get(request).cardTextColumns=new Set(columns)};GRID_REPORTING.exportColumn=function(grid,column){const policy=grid.columnLayouts?.get(column.id),presenter=column.presenter||grid.view.reporting?.presenters?.[column.id];const minimum=column.minWidth??policy?.minimum??40,maximum=column.maxWidth??policy?.maximum;return{id:column.id,title:column.title,kind:column.kind,width:{unit:"css-px",preferred:column.renderWidth??column.width,minimum,...(maximum===undefined?{}:{maximum})},pinned:column.effectivePin==="left"||column.effectivePin==="right"?column.effectivePin:null,wrap:!!column.wrap,alignment:column.kind==="number"?"right":"left",...(column.format?{format:{...column.format}}:{}),...(presenter?{presenter:JSON.parse(JSON.stringify(presenter))}:{}),...JSON.parse(JSON.stringify({valueLabels:column.valueLabels,valueTones:column.valueTones,numericTones:column.numericTones})),...(column.render&&column.hostRenderer!==false?{diagnostics:["custom-renderer"]}:{})}};GRID_REPORTING.exportRecord=function(grid,request,row){const capture=GRID_EXPORT_PRESENTATION.get(request),matches=new Map(),cells=Object.create(null),templateText=capture.cardTextColumns?Object.create(null):null;for(const rule of capture.rules){const key=rule.target==="cell"?rule.column:null;if(!matches.has(key)&&gridMatches(row,rule,capture.columns.find(column=>column.id===rule.column)))matches.set(key,rule)}
|
|
1456
|
+
const fmt=formatting(request.locale||capture.locale,request.timeZone==="local",capture.labels);for(const column of request.columns){const cell=row.cells[column.id],object=isCellObject(cell),value=cellValue(cell)??null,presenter=column.presenter;let text=cellText(cell,column,fmt);if(capture.cardTextColumns?.has(column.id))templateText[column.id]=text;const missing=presenter&&(value===null&&(!object||cell.text===undefined)||text==="");const diagnostics=[...(column.diagnostics||[])];if(object&&cell.className)diagnostics.push("custom-class");if(object&&cell.badges?.length)diagnostics.push("badges");if(object&&cell.series)diagnostics.push("history-series");if(missing)text="—";if(!missing&&presenter?.kind==="boolean"&&typeof value==="boolean")text=value?capture.labels.yes:capture.labels.no;if(!missing&&presenter?.kind==="identifier"){const secondary=presenter.secondaryColumn?cellText(row.cells[presenter.secondaryColumn],capture.columns.find(item=>item.id===presenter.secondaryColumn),fmt):object&&cell.secondary;if(secondary)text +="\n"+secondary}
|
|
1405
1457
|
if(!missing&&presenter?.kind==="change"){const delta=cellValue(row.cells[presenter.deltaColumn]);if(typeof delta==="number"&&Number.isFinite(delta)){text=(delta===0?"":delta>0?"↑ ":"↓ ")+ numberFormat(request.locale||capture.locale).format(Math.abs(delta))+ " "+text;if(delta!==0)diagnostics.push("partial-tone")}}
|
|
1406
1458
|
let link;if(!missing&&presenter?.kind==="link"){const target=cellValue(presenter.hrefColumn?row.cells[presenter.hrefColumn]:cell);if(typeof target==="string"&&/^[a-z][a-z\d+.-]*:/i.test(target.trim())) {
|
|
1407
1459
|
const safe=gridPresenterLink(target);if(safe&&/^(https?:|mailto:)/i.test(safe)) link = { target: safe, ...(object && cell.title ? { tooltip: cell.title } : {}) };
|
|
1408
1460
|
else diagnostics.push("unsafe-link")}else if(target)diagnostics.push("relative-link")}
|
|
1409
1461
|
const mark=matches.get(column.id),tone=mark?.tone??columnValueTone(column,cell)??(presenter?.kind==="boolean"&&value===true&&presenter.inverted?"danger":
|
|
1410
1462
|
presenter?gridPresenterTone(presenter,value,object?cell.tone:null):object?cell.tone:undefined);if(mark?.className)diagnostics.push("custom-class");cells[column.id]={value,text,...(tone?{tone}:{}),...(link?{link}:{}),...(diagnostics.length?{diagnostics:[...new Set(diagnostics)]}:{})}}
|
|
1411
|
-
const mark=matches.get(null),tone=mark?.tone??row.tone,diagnostics=row.className||mark?.className?["custom-class"]:[];return gridFreeze({id:row.id,cells,...(tone?{tone}:{}),...(diagnostics.length?{diagnostics}:{})})};const DELIVERY_PREPARE=DataGrid.prototype.prepareExport,DELIVERY_TEXT=DataGrid.prototype.exportText,DELIVERY_API=GRID_REPORTING.api,DELIVERY_CALLBACKS=new WeakMap();function reportingExportTotal(request){return request.recordCount}
|
|
1463
|
+
const mark=matches.get(null),tone=mark?.tone??row.tone,diagnostics=row.className||mark?.className?["custom-class"]:[];return gridFreeze({id:row.id,cells,...(templateText?{templateText}:{}),...(tone?{tone}:{}),...(diagnostics.length?{diagnostics}:{})})};const GRID_SHOW_AS={},GRID_SHOW_AS_MODES=["value","percentGrandTotal","percentParent","percentColumn","percentRow","runningTotal","rank","difference","percentChange"];const VALUES_PART=definePart(DataGrid,"values","aggregate display modes",()=>({DataGrid,GRID_SHOW_AS,GRID_SHOW_AS_MODES,h,setText,gridFreeze,reportingAggregateParts,reportingAggregateName,gridButton}));DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridShowAs:"Show values as",gridShowAsValue:"Value",gridShowAsPercentGrandTotal:"% of grand total",gridShowAsPercentParent:"% of parent total",gridShowAsPercentColumn:"% of column total",gridShowAsPercentRow:"% of row total",gridShowAsRunningTotal:"Running total",gridShowAsRank:"Rank",gridShowAsDifference:"Difference from previous",gridShowAsPercentChange:"% change from previous"}));const showAsMode=(grid,aggregate)=>grid.aggregateShowAs?.[aggregate.id]??aggregate.showAs??"value";const showAsCaption=(grid,mode)=>mode==="value"?"":grid.labels["gridShowAs"+mode[0].toUpperCase()+ mode.slice(1)];const VALUES_VALIDATE=GRID_REPORTING.validate;GRID_REPORTING.validate=view=>{VALUES_VALIDATE(view);if(view.reporting?.aggregates?.some(aggregate=>aggregate.showAs!==undefined&&!GRID_SHOW_AS_MODES.includes(aggregate.showAs)))throw new Error("CanopyX: unknown aggregate display mode.")};const VALUES_PAGE=DataGrid.prototype.requestPage;DataGrid.prototype.requestPage=async function(query,signal){if(this.view.reporting?.aggregates?.some(aggregate=>showAsMode(this,aggregate)!=="value"))await loadPart(VALUES_PART);return VALUES_PAGE.call(this,query,signal)};Object.assign(DataGrid.prototype,{async showAggregateMenu(column,aggregates,values,button,text){await loadPart(VALUES_PART);await this.requirePart("filter-editor");if(!this.destroyed&&!this.pendingQuery)this.openAggregateMenu(column,aggregates,values,button,text)},async setAggregateShowAs(id,mode){await loadPart(VALUES_PART);if(this.destroyed||this.pendingQuery||!this.view.reporting?.aggregates?.some(aggregate=>aggregate.id===id)||!GRID_SHOW_AS_MODES.includes(mode))throw new Error("CanopyX: choose a declared aggregate and display mode on an accepted view.");this.aggregateShowAs=Object.assign(Object.create(null),this.aggregateShowAs,{[id]:mode});this.aggregateDisplayCache=null;this.version++;this.render();this.syncSummaryControls?.();this.stateChanged();this.noteViewPreferenceIntent?.();this.emit("aggregateDisplay",{aggregate:id,mode})},async getDisplaySummary(){await loadPart(VALUES_PART);return this.summary?GRID_SHOW_AS.summary(this,this.summary).result:null}});const VALUES_STATE={get:DataGrid.prototype.getState,read:DataGrid.prototype.readGridState,apply:DataGrid.prototype.applyStatePresentation,share:DataGrid.prototype.getShareState};Object.assign(DataGrid.prototype,{getState(){const state=VALUES_STATE.get.call(this);if(this.aggregateShowAs&&Object.keys(this.aggregateShowAs).length)state.aggregateShowAs={...this.aggregateShowAs};return state},readGridState(input){const state=VALUES_STATE.read.call(this,input),modes=input.aggregateShowAs;if(modes===undefined)return state;if(!modes||typeof modes!=="object"||Array.isArray(modes)||Object.keys(modes).length>32||Object.values(modes).some(mode=>!GRID_SHOW_AS_MODES.includes(mode)))throw new Error("CanopyX: invalid saved aggregate display modes.");state.aggregateShowAs=Object.fromEntries(Object.entries(modes).filter(([id])=>this.view.reporting?.aggregates?.some(aggregate=>aggregate.id===id)));return state},applyStatePresentation(state){this.aggregateShowAs=Object.assign(Object.create(null),state.aggregateShowAs);this.aggregateDisplayCache=null;return VALUES_STATE.apply.call(this,state)},getShareState(){const state=VALUES_STATE.share.call(this);return this.aggregateShowAs&&Object.keys(this.aggregateShowAs).length?gridFreeze({...state,aggregateShowAs:{...this.aggregateShowAs}}):state}});const VALUES_API=GRID_REPORTING.api;GRID_REPORTING.api=(api,grid)=>{VALUES_API?.(api,grid);if(grid.view.reporting?.aggregates?.length){api.setAggregateShowAs=(id,mode)=>grid.setAggregateShowAs(id,mode);api.getDisplaySummary=()=>grid.getDisplaySummary()}};const DELIVERY_PREPARE=DataGrid.prototype.prepareExport,DELIVERY_TEXT=DataGrid.prototype.exportText,DELIVERY_API=GRID_REPORTING.api,DELIVERY_CALLBACKS=new WeakMap(),DELIVERY_PRINT=DataGrid.prototype.print,DELIVERY_EXPORT_AS=DataGrid.prototype.exportAs;function reportingExportTotal(request){return request.recordCount}
|
|
1412
1464
|
function reportingFileName(value){if(typeof value!=="string"||!value||value.length>100||value!==value.trim()||
|
|
1413
1465
|
/[\\/:*?"<>|\x00-\x1f\x7f-\x9f\u2028\u2029\u202a-\u202e\u2066-\u2069]/u.test(value) ||
|
|
1414
1466
|
/^\.|[. ]$/.test(value) || /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(value))
|
|
1415
1467
|
throw new Error("CanopyX: export file name must be a safe basename of at most 100 characters.");requireIdentifier(value,"export file name");return value}
|
|
1416
1468
|
function reportingExportProgress(grid,request,callback,signal){const total=reportingExportTotal(request);let completed=0,phase="reading";const order={reading:0,generating:1,ready:2};return(next,count=completed)=>{if(grid.destroyed||signal?.aborted)return;if(!Object.hasOwn(order,next)||order[next]<order[phase]||!Number.isSafeInteger(count)||count<completed||
|
|
1417
1469
|
total!==null&&count>total)throw new Error("CanopyX: invalid export progress.");completed=count;phase=next;callback?.(Object.freeze({phase,completed,total}));return completed}}
|
|
1418
|
-
async function reportingExportSnapshot(grid,exported,maxRows,signal,progress){
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
GRID_REPORTING.api=(api,grid)=>{DELIVERY_API?.(api,grid);api.materializeExport=grid.materializeExport.bind(grid);api.print=grid.print.bind(grid)};Object.assign(DataGrid.prototype,{prepareExport(options){if(options?.onProgress!==undefined&&typeof options.onProgress!=="function")throw new Error("CanopyX: export progress needs a callback.");const exported=DELIVERY_PREPARE.call(this,options);let locale=this.lang;if(options.locale!==undefined){if(typeof options.locale!=="string"||options.locale.length>128)
|
|
1470
|
+
async function reportingExportSnapshot(grid,exported,maxRows,signal,progress){await loadExportPart();return EXPORT_IMPL.snapshot(grid,exported,maxRows,signal,progress)}
|
|
1471
|
+
GRID_REPORTING.api=(api,grid)=>{DELIVERY_API?.(api,grid);api.materializeExport=grid.materializeExport.bind(grid);api.print=grid.print.bind(grid)};Object.assign(DataGrid.prototype,{exportAs(format,options){if(options?.pivot===true&&(format==="xlsx"||format==="pdf"))
|
|
1472
|
+
return Promise.reject(new Error("CanopyX: pivot XLSX/PDF export is not supported yet."));return DELIVERY_EXPORT_AS.call(this,format,options)},prepareExport(options){if(options?.onProgress!==undefined&&typeof options.onProgress!=="function")throw new Error("CanopyX: export progress needs a callback.");const ordinary=options?.pivot===true?null:DELIVERY_PREPARE.call(this,options);let locale=this.lang;if(options.locale!==undefined){if(typeof options.locale!=="string"||options.locale.length>128)
|
|
1422
1473
|
throw new Error("CanopyX: export locale must be supported by this browser.");try{locale=Intl.getCanonicalLocales(options.locale)[0];if(!Intl.NumberFormat.supportedLocalesOf(locale).length)throw new Error()}catch{throw new Error("CanopyX: export locale must be supported by this browser.")}}
|
|
1423
|
-
if(options.timeZone!==undefined&&!["utc","local"].includes(options.timeZone))throw new Error("CanopyX: export time zone is utc or local.");const fileName=reportingFileName(options.fileName??("canopyx-"+this.view.id.replace(/[^a-z0-9_-]/gi, "_")).slice(0, 100));
|
|
1424
|
-
const request=gridFreeze({...exported.request,locale,fileName,timeZone:options.timeZone||exported.request.timeZone,columns:exported.request.columns.map(column=>GRID_REPORTING.exportColumn(this,this.columns.find(item=>item.id===column.id)))}),grid=this,signal=options.signal,callback=options.onProgress;GRID_REPORTING.captureExport(this,request);const result=Object.freeze({request,rows:async function*(options){const enumerationSignal=gridExportSignal(signal,options);const progress=reportingExportProgress(grid,request,callback,enumerationSignal);let count=0;progress("reading",0);for await(const row of grid.exportRows(request,enumerationSignal)){yield row;if(++count%100===0)progress("reading",count)}
|
|
1425
|
-
progress("generating",count)}});DELIVERY_CALLBACKS.set(result,callback);return result},async exportText(exported,maxRows,delimiter,signal){let count=0;const rows=async function*(){for await(const row of exported.rows()){count++;yield row}};const text=await DELIVERY_TEXT.call(this,{request:exported.request,rows},maxRows,delimiter,signal);reportingExportProgress(this,exported.request,DELIVERY_CALLBACKS.get(exported),signal)("ready",count);return text},materializeExport(options){const exported=this.prepareExport(options),maxRows=options.maxRows??1000,callback=DELIVERY_CALLBACKS.get(exported);return this.runGridHook(async signal=>{const progress=reportingExportProgress(this,exported.request,callback,signal);const result=await reportingExportSnapshot(this,exported,maxRows,signal,progress);progress("ready",result.rows.length);return result},options.signal)},print(options){const exported=this.prepareExport(options),maxRows=options.maxRows??1000,callback=DELIVERY_CALLBACKS.get(exported)
|
|
1426
|
-
|
|
1474
|
+
if(options.timeZone!==undefined&&!["utc","local"].includes(options.timeZone))throw new Error("CanopyX: export time zone is utc or local.");const pivot=options.pivot===true?GRID_REPORTING.preparePivotExport?.(this,{...options,locale,timeZone:options.timeZone||(this.local?"local":"utc")}):null;if(options.pivot===true&&!pivot)throw new Error("CanopyX: pivot table capture requires an active pivot producer.");const exported=pivot||ordinary;const fileName=reportingFileName(options.fileName??("canopyx-"+this.view.id.replace(/[^a-z0-9_-]/gi, "_")).slice(0, 100));
|
|
1475
|
+
const request=gridFreeze({...exported.request,locale,fileName,timeZone:options.timeZone||exported.request.timeZone,formatRules:pivot?exported.request.formatRules||[]:this.getFormatRules(),generatedAt:new Date().toISOString(),columns:pivot?exported.request.columns:exported.request.columns.map(column=>GRID_REPORTING.exportColumn(this,this.columns.find(item=>item.id===column.id)))}),grid=this,signal=options.signal,callback=options.onProgress;if(!pivot)GRID_REPORTING.captureExport(this,request);const result=Object.freeze({request,rows:async function*(options){const enumerationSignal=gridExportSignal(signal,options);const progress=reportingExportProgress(grid,request,callback,enumerationSignal);let count=0;progress("reading",0);for await(const row of grid.exportRows(request,enumerationSignal)){yield row;if(++count%100===0)progress("reading",count)}
|
|
1476
|
+
progress("generating",count)}});DELIVERY_CALLBACKS.set(result,callback);return result},async exportText(exported,maxRows,delimiter,signal){let count=0;const rows=async function*(){for await(const row of exported.rows()){count++;yield row}};const text=await DELIVERY_TEXT.call(this,{request:exported.request,rows},maxRows,delimiter,signal);reportingExportProgress(this,exported.request,DELIVERY_CALLBACKS.get(exported),signal)("ready",count);return text},materializeExport(options){const exported=this.prepareExport(options),maxRows=options.maxRows??1000,callback=DELIVERY_CALLBACKS.get(exported);return this.runGridHook(async signal=>{const progress=reportingExportProgress(this,exported.request,callback,signal);const result=await reportingExportSnapshot(this,exported,maxRows,signal,progress);progress("ready",result.rows.length);return result},options.signal)},print(options){if(typeof this.opts.onPrint!=="function")return DELIVERY_PRINT.call(this,options);const exported=this.prepareExport(options),maxRows=options.maxRows??1000,callback=DELIVERY_CALLBACKS.get(exported),layout=this.layoutMode==="cards"?"stacked":"table";return this.runGridHook(async signal=>{const progress=reportingExportProgress(this,exported.request,callback,signal);const snapshot=await reportingExportSnapshot(this,exported,maxRows,signal,progress);await this.opts.onPrint({snapshot,signal,layout});gridOperationActive(this,signal);progress("ready",snapshot.rows.length)},options.signal)},exportToHost(format,options){const exported=this.prepareExport(options),{request}=exported,callback=DELIVERY_CALLBACKS.get(exported);if(typeof format!=="string"||!format.trim()||typeof this.opts.onExport!=="function")throw new Error("CanopyX: host export needs a format and an onExport hook.");return this.runGridHook(async signal=>{const progress=reportingExportProgress(this,request,callback,signal);progress("reading",0);let completed=0;await this.opts.onExport({format,request,capture:exported,signal,reportProgress:(phase,count)=>{completed=progress(phase,count)}});gridOperationActive(this,signal);progress("ready",reportingExportTotal(request)??completed)},options.signal,exportTimeout(this,options))}});const GRID_PIVOT={};const pivotEnabled=view=>!!view.reporting?.pivot||!!view.reporting?.pivotable;function pivotDefinition(view,input){if(!pivotEnabled(view)||!input||typeof input!=="object"||Array.isArray(input)||
|
|
1477
|
+
Object.keys(input).some(key=>!["rows","columns","values","maxColumns","topNColumns","sortByTotal"].includes(key)))throw new Error("CanopyX: invalid pivot definition.");const axis=(value,max)=>{if(!Array.isArray(value)||value.length>max)throw new Error("CanopyX: invalid pivot axis.");const levels=reportingGroupLevels(view,value);if(levels.some(level=>level.aggregate!==undefined||view.columns.find(column=>column.id===level.column).filterable===false))throw new Error("CanopyX: pivot axes need sortable and filterable columns.");return levels};const rows=axis(input.rows,4),columns=axis(input.columns,2),values=input.values,maxColumns=input.maxColumns??200,topNColumns=input.topNColumns;if(!Array.isArray(values)||!values.length||values.length>32||new Set(values).size!==values.length||
|
|
1478
|
+
values.some(id=>!view.reporting.aggregates?.some(aggregate=>aggregate.id===id))||
|
|
1479
|
+
!Number.isInteger(maxColumns)||maxColumns<1||maxColumns>200||
|
|
1480
|
+
topNColumns!==undefined&&(!Number.isInteger(topNColumns)||topNColumns<1||topNColumns>200))throw new Error("CanopyX: invalid pivot values or column limit.");const sort=input.sortByTotal;if(sort!==undefined&&(!sort||typeof sort!=="object"||Array.isArray(sort)||Object.keys(sort).some(key=>!["aggregate","direction"].includes(key))||
|
|
1481
|
+
!values.includes(sort.aggregate)||!["asc","desc"].includes(sort.direction)))throw new Error("CanopyX: invalid pivot total sort.");return gridFreeze({rows,columns,values:[...values],maxColumns,...(topNColumns===undefined?{}:{topNColumns}),...(sort===undefined?{}:{sortByTotal:{...sort}})})}
|
|
1482
|
+
GRID_REPORTING.pivotDefinition=pivotDefinition;GRID_REPORTING.preparePivotExport=(grid,options)=>{if(!GRID_PIVOT.prepareExport)throw new Error("CanopyX: pivot table capture needs an accepted active pivot.");return GRID_PIVOT.prepareExport(grid,options,column=>{const numeric=column.kind==="number";return{...column,width:{unit:"css-px",preferred:numeric?120:240,minimum:40},wrap:!numeric,alignment:numeric?"right":"left"}})};function pivotDefaultState(view){return view.reporting?.pivot?{...pivotDefinition(view,view.reporting.pivot),active:true,collapsedRows:[],collapsedColumns:[]}:null}
|
|
1483
|
+
function pivotIntent(grid){if(grid.pivotState===undefined)grid.pivotState=pivotDefaultState(grid.view);return grid.pivotState}
|
|
1484
|
+
function pivotRequestOf(grid,query,revision,state=pivotIntent(grid)){const{active,collapsedRows,collapsedColumns,columnKey,...definition}=state;return{query:gridQuery(grid.view,{...query,revision}),definition:pivotDefinition(grid.view,definition)}}
|
|
1485
|
+
const PIVOT_PART=definePart(DataGrid,"pivot","pivot analysis",()=>({DataGrid,GRID_PIVOT,GRID_ROWS,GRID_SHOW_AS,GRID_SHOW_AS_MODES,GRID_BUCKETS,GRID_CALCULATIONS,UI_IMPL,UI_TOOLS,h,setText,fill,gridButton,gridFreeze,gridQuery,gridQueryStart,gridQueryRows,gridScalar,cellValue,cellText,gridKeyCompare,gridOrder,reportingAggregateName,reportingValueText,numberFormat,formatting,gridExportSignal,validateGridPage,checkDocument,validStructuralCount,gridOperationActive,pivotDefinition,pivotIntent,pivotRequestOf,showAsMode,showAsCaption}));const PIVOT_VALIDATE=GRID_REPORTING.validate,PIVOT_CHROME=DataGrid.prototype.buildGridChrome,PIVOT_REQUEST=DataGrid.prototype.requestPage;GRID_REPORTING.validate=view=>{PIVOT_VALIDATE(view);if(view.reporting.pivotable!==undefined&&typeof view.reporting.pivotable!=="boolean"||pivotEnabled(view)&&(!view.reporting.aggregates?.length||view.source.navigation==="cursor"))throw new Error("CanopyX: pivots need offset reporting with declared aggregates.");if(view.reporting.pivot!==undefined)pivotDefinition(view,view.reporting.pivot)};Object.assign(DataGrid.prototype,{async loadPivot(){await this.loadRows();await loadPart(VALUES_PART);await loadPart(BUCKETS_PART);await loadPart(PIVOT_PART)},buildGridChrome(){PIVOT_CHROME.call(this);if(!pivotEnabled(this.view))return;const button=gridButton(this.labels.gridPivot,()=>this.loadPivot().then(()=>this.configurePivot(button)).catch(error=>this.emit("error",{error})));button.classList.add("cx-pivot-toggle");this.toolbar.append(button);this.pivotButton=button},async requestPage(query,signal){const restore=this.stateQueryRestore?.epoch===this.queryEpoch?this.stateQueryRestore:null;const state=restore?restore.state.pivot||pivotDefaultState(this.view):pivotIntent(this);if(!pivotEnabled(this.view)||!state?.active||signal!==this.dataLifetime.signal)return PIVOT_REQUEST.call(this,query,signal);await this.loadPivot();const page=validateGridPage(await GRID_ROWS.base.requestPage.call(this,query,signal),this.view,query);await this.requestPivot(query,page,signal,restore,state);return page},async setPivot(input){if(!pivotEnabled(this.view))throw new Error("CanopyX: this view does not enable pivots.");const current=pivotIntent(this);if(input!==false)pivotDefinition(this.view,input);await this.loadPivot();if(this.destroyed||this.pendingQuery)throw new Error("CanopyX: change pivot on an accepted view.");const previous=current&&JSON.parse(JSON.stringify(current));this.pivotState=input===false?current&&{...current,active:false}:{...pivotDefinition(this.view,input),active:true,collapsedRows:[],collapsedColumns:[]};try{await this.setQuery(gridQueryStart(this.queryForChange(true),{}))}
|
|
1486
|
+
catch(error){this.pivotState=previous;this.render();throw error}
|
|
1487
|
+
this.noteViewPreferenceIntent?.();this.stateChanged();this.emit("pivot",{pivot:this.getPivot()})},getPivot(){const state=pivotIntent(this);return state?gridFreeze(JSON.parse(JSON.stringify(state))):null}});const PIVOT_STATE={get:DataGrid.prototype.getState,read:DataGrid.prototype.readGridState,apply:DataGrid.prototype.applyStatePresentation,share:DataGrid.prototype.getShareState};Object.assign(DataGrid.prototype,{getState(){const state=PIVOT_STATE.get.call(this),pivot=pivotEnabled(this.view)&&this.getPivot();if(pivot)state.pivot=pivot;return state},readGridState(input){const state=PIVOT_STATE.read.call(this,input);if(input.pivot===undefined)return state;const{active,collapsedRows=[],collapsedColumns=[],columnKey,...definition}=input.pivot||{};const keys=values=>Array.isArray(values)&&values.length<=10000&&values.every(value=>typeof value==="string"&&value.length>0&&value.length<=65536);if(typeof active!=="boolean"||!keys(collapsedRows)||!keys(collapsedColumns)||columnKey!==undefined&&(typeof columnKey!=="string"||columnKey.length>65536))throw new Error("CanopyX: invalid saved pivot state.");state.pivot={...pivotDefinition(this.view,definition),active,collapsedRows:[...new Set(collapsedRows)],collapsedColumns:[...new Set(collapsedColumns)],...(columnKey===undefined?{}:{columnKey})};return state},applyStatePresentation(state){const restore=this.stateQueryRestore;if(restore?.state===state&&restore.pivotCandidate)this.pivotCandidate=restore.pivotCandidate;this.pivotState=state.pivot?JSON.parse(JSON.stringify(state.pivot)):undefined;return PIVOT_STATE.apply.call(this,state)},getShareState(){const state=PIVOT_STATE.share.call(this),pivot=pivotEnabled(this.view)&&this.getPivot();return pivot?gridFreeze({...state,pivot}):state}});const PIVOT_API=GRID_REPORTING.api;GRID_REPORTING.api=(api,grid)=>{PIVOT_API?.(api,grid);if(!pivotEnabled(grid.view))return;api.getPivot=()=>grid.getPivot();api.setPivot=definition=>grid.setPivot(definition);api.getPivotResult=()=>grid.pivotResult||null;api.getPivotExport=()=>grid.loadPivot().then(()=>grid.pivotExport());api.setPivotCollapsed=(axis,key,collapsed)=>grid.loadPivot().then(()=>grid.setPivotCollapsed(axis,key,collapsed))};const TOOLBAR_CHROME=DataGrid.prototype.buildGridChrome,TOOLBAR_DESTROY=DataGrid.prototype.destroy,TOOLBAR_ACTION=DataGrid.prototype.gridAction,TOOLBAR_API=GRID_REPORTING.api;Object.assign(DataGrid.prototype,{buildGridChrome(){TOOLBAR_CHROME.call(this);const first=this.toolbar.firstChild,search=this.toolbar.querySelector(".cx-search");this.slots={__proto__:null};for(const[name,before]of[["start",first],["afterSearch",search?search.nextSibling:first],["afterViews",this.spacer],["end",this.selectionBar]]){const slot=this.slots[name]=h("div","cx-toolbar-slot");slot.dataset.cxToolbarSlot=name;before.before(slot)}
|
|
1427
1488
|
this.slotted=[...this.host.querySelectorAll(":scope > [data-cx-slot]")];this.slotted.map(node=>this.toolbarSlot(node.dataset.cxSlot)).forEach((slot,index)=>slot.appendChild(this.slotted[index]))},toolbarSlot(name){const slot=this.slots[name];if(!slot)throw new Error("CanopyX: toolbar slots are start, afterSearch, afterViews and end.");return slot},destroy(){if(!this.destroyed&&this.slotted)this.host.append(...this.slotted.filter(node=>this.toolbar.contains(node)));return TOOLBAR_DESTROY.call(this)},gridAction(action,act){const render=action?.render;if(render===undefined)return TOOLBAR_ACTION.call(this,action,act);if(typeof action.label!=="string"||typeof render!=="function"||action.run!==undefined)
|
|
1428
1489
|
throw new Error("CanopyX: an action has a label and either a run or a render function.");const entry=h("details","cx-grid-action"),summary=h("summary","cx-button"),body=h("div","cx-grid-action-body"),menu=this.overflow.details;setText(summary,action.label);summary.setAttribute("role","button");entry.append(summary,body);entry.addEventListener("toggle",()=>{if(!entry.open)return;if(!body.drawn){body.drawn=true;try{render(body,{close:()=>{entry.open=false;menu.open=false;menu.firstChild.focus()},signal:this.lifetime.signal})}
|
|
1429
1490
|
catch(error){body.drawn=false;body.replaceChildren();this.emit("error",{error})}}
|
|
1430
1491
|
(body.querySelector(":is(input:not([type=hidden]),select,textarea,button):not(:disabled),[href],[tabindex]:not([tabindex=\"-1\"])")||summary).focus()});entry.addEventListener("keydown",event=>{if(event.key==="Escape"&&entry.open){entry.open=false;summary.focus();event.preventDefault();event.stopPropagation()}});return entry}});GRID_REPORTING.api=(api,grid)=>{TOOLBAR_API(api,grid);api.toolbarSlot=name=>grid.toolbarSlot(name)};class FlowHost{}
|
|
1431
|
-
const FORMS_IMPL={},FLOW_IMPL={},activeFlows=new WeakMap();const FORMS_PART=definePart(FlowHost,"forms","forms",()=>({FORMS_IMPL,h,setText,fill,gridFreeze,gridMomentFromParts,boundedPattern,foldText}));const FLOW_VIEWS=typeof FLOW_GRID_VIEWS==="object"?FLOW_GRID_VIEWS:Object.freeze({grid:null,tree:null,reporting:false});const FLOW_PART=definePart(FlowHost,"flow","flow view",()=>({FLOW_IMPL,FORMS_IMPL,h,setText,fill,plural,foldText,boundedPattern,numberFormat,gridFreeze}));const FLOW_FEATURES={};for(const name of["host","checks","options","pick","records","drafts","preview","command","tasks","dialog","links","endpoint","consent"])
|
|
1432
|
-
FLOW_FEATURES[name]=definePart(FlowHost,"flow-"+name,"flow "+name,()=>({FLOW_IMPL,FORMS_IMPL,h,setText,fill,plural,numberFormat,FLOW_VIEWS}));if(typeof DataGrid==="function")DataGrid.prototype.parts.forms=FORMS_PART;const FLOW_DOC_PARTS={command:"command",presets:"drafts",draft:"drafts",deepLinks:"links",endpoint:"endpoint"},FLOW_DOC_PLAIN={draft:"none",deepLinks:false};function flowFeatures(doc,hooks){const names=new Set(),add=(name,on)=>on&&names.add(name),list=value=>Array.isArray(value)?value:[];const object=value=>value!==null&&typeof value==="object"&&!Array.isArray(value),has=(value,...keys)=>object(value)&&keys.some(key=>value[key]!==undefined);for(const[key,name]of Object.entries(FLOW_DOC_PARTS))add(name,doc[key]!==undefined&&doc[key]!==FLOW_DOC_PLAIN[key]);add("tasks",doc.mode==="tasks");add("dialog",has(doc.layout,"launch")||has(doc.layout,"container")&&doc.layout.container!=="inline");add("preview",list(doc.endpoint?.routes).includes("preview"));for(const step of list(doc.steps).filter(object)){add("checks",step.checks!==undefined);add("pick",step.type==="pick");add("preview",step.preview===true);add("consent",has(step,"confirm","acknowledge"));for(const block of[step,...list(step.sections)]){if(block!==step)add("options",has(block,"source","dependsOn"));for(const field of object(block)?list(block.fields):[]){add("options",has(field,"dependsOn")||has(field,"source")&&field.source!=="document");add("records",object(field)&&field.type==="records")}}}
|
|
1492
|
+
const FORMS_IMPL={},FLOW_IMPL={},activeFlows=new WeakMap();const FORMS_PART=definePart(FlowHost,"forms","forms",()=>({FORMS_IMPL,h,setText,fill,gridFreeze,gridMomentFromParts,boundedPattern,foldText}));const FLOW_VIEWS=typeof FLOW_GRID_VIEWS==="object"?FLOW_GRID_VIEWS:Object.freeze({grid:null,tree:null,reporting:false});const FLOW_PART=definePart(FlowHost,"flow","flow view",()=>({FLOW_IMPL,FORMS_IMPL,h,setText,fill,plural,foldText,boundedPattern,numberFormat,gridFreeze}));const FLOW_FEATURES={};for(const name of["host","checks","options","pick","records","drafts","preview","command","tasks","dialog","links","endpoint","consent","facts"])
|
|
1493
|
+
FLOW_FEATURES[name]=definePart(FlowHost,"flow-"+name,"flow "+name,()=>({FLOW_IMPL,FORMS_IMPL,h,setText,fill,plural,numberFormat,FLOW_VIEWS}));if(typeof DataGrid==="function")DataGrid.prototype.parts.forms=FORMS_PART;const FLOW_DOC_PARTS={command:"command",presets:"drafts",draft:"drafts",deepLinks:"links",endpoint:"endpoint"},FLOW_DOC_PLAIN={draft:"none",deepLinks:false};function flowFeatures(doc,hooks){const names=new Set(),add=(name,on)=>on&&names.add(name),list=value=>Array.isArray(value)?value:[];const object=value=>value!==null&&typeof value==="object"&&!Array.isArray(value),has=(value,...keys)=>object(value)&&keys.some(key=>value[key]!==undefined);for(const[key,name]of Object.entries(FLOW_DOC_PARTS))add(name,doc[key]!==undefined&&doc[key]!==FLOW_DOC_PLAIN[key]);add("tasks",doc.mode==="tasks");add("dialog",has(doc.layout,"launch")||has(doc.layout,"container")&&doc.layout.container!=="inline");add("preview",list(doc.endpoint?.routes).includes("preview"));for(const step of list(doc.steps).filter(object)){add("facts",step.facts!==undefined);add("checks",step.checks!==undefined);add("pick",step.type==="pick");add("preview",step.preview===true);add("consent",has(step,"confirm","acknowledge"));for(const block of[step,...list(step.sections)]){if(block!==step)add("options",has(block,"source","dependsOn"));for(const field of object(block)?list(block.fields):[]){add("options",has(field,"dependsOn")||has(field,"source")&&field.source!=="document");add("records",object(field)&&field.type==="records")}}}
|
|
1433
1494
|
hooks=object(hooks)?hooks:{};return{required:[...names],optional:[typeof hooks.preview==="function"&&"preview",object(hooks.presetStore)&&"drafts"].filter(name=>name&&!names.has(name))}}
|
|
1434
|
-
const FLOW_ASYNC_METHODS=["next","back","goTo","setState","validate","submit","runCheck","refreshOptions","saveDraft","setPreset"];const FLOW_SYNC_METHODS=["getStep","getValues","setValues","getState","isDirty","reset","setFieldError","setProgress","complete","setStepState","open","close"];function createFlow(host,options){if(!host||!options||!options.flow||typeof options.flow!=="object")throw new Error("CanopyX: createFlow needs a host element and a flow document.");const existing=activeFlows.get(host);if(existing)return existing;const disposeTheme=watchTheme(host);let impl=null,destroyed=false,mountError=null;const destroyedError=()=>new Error("CanopyX: this flow has been destroyed.");const requireImpl=name=>{if(destroyed)throw destroyedError();if(!impl)throw new Error("CanopyX: flow is not ready; await api.ready before calling "+name+"().");return impl};const editors=Object.assign({},...[...PLUGINS.values()].map(item=>item.editors||{}));const{required,optional}=flowFeatures(options.flow,options.hooks);const ready=Promise.all([loadPart(FLOW_PART),...required.map(name=>loadPart(FLOW_FEATURES[name])),...optional.map(name=>loadPart(FLOW_FEATURES[name])
|
|
1495
|
+
const FLOW_ASYNC_METHODS=["next","back","goTo","setState","validate","submit","runCheck","refreshOptions","saveDraft","setPreset"];const FLOW_SYNC_METHODS=["getStep","getValues","setValues","getState","isDirty","reset","setFieldError","setProgress","complete","setStepState","invalidateChecks","open","close"];function createFlow(host,options){if(!host||!options||!options.flow||typeof options.flow!=="object")throw new Error("CanopyX: createFlow needs a host element and a flow document.");const existing=activeFlows.get(host);if(existing)return existing;const disposeTheme=watchTheme(host);let impl=null,destroyed=false,mountError=null;const destroyedError=()=>new Error("CanopyX: this flow has been destroyed.");const requireImpl=name=>{if(destroyed)throw destroyedError();if(!impl)throw new Error("CanopyX: flow is not ready; await api.ready before calling "+name+"().");return impl};const editors=Object.assign({},...[...PLUGINS.values()].map(item=>item.editors||{}));const{required,optional}=flowFeatures(options.flow,options.hooks);const ready=Promise.all([loadPart(FLOW_PART),...required.map(name=>loadPart(FLOW_FEATURES[name])),...optional.map(name=>loadPart(FLOW_FEATURES[name])
|
|
1435
1496
|
.catch(error=>host.dispatchEvent(new CustomEvent("canopyx:error",{detail:{error,hook:"part"},bubbles:true}))))]).then(async()=>{if(destroyed)throw destroyedError();impl=FLOW_IMPL.create(host,options,editors);return impl.ready});const api={element:host,flow:options.flow,view:options.flow};for(const name of FLOW_SYNC_METHODS)api[name]=(...args)=>requireImpl(name)[name](...args);for(const name of FLOW_ASYNC_METHODS)api[name]=(...args)=>{if(destroyed)return Promise.reject(destroyedError());return ready.then(()=>requireImpl(name)[name](...args))};api.ready=ready.then(()=>{if(destroyed)throw destroyedError();return api});api.ready.catch(()=>{});api.destroy=()=>{if(destroyed)return;destroyed=true;disposeTheme();try{impl?.destroy()}
|
|
1436
1497
|
finally{impl=null;mountError?.remove();if(activeFlows.get(host)===api){activeFlows.delete(host);if(host.canopyx===api)delete host.canopyx;host.removeAttribute("data-canopyx-mounted");host.removeAttribute("data-cx-ready");host.removeAttribute("data-cx-error");host.classList.remove("cx-flow-host")}}};ready.catch(error=>{if(destroyed)return;const notice=mountError=h("div","cx-mount-error");notice.setAttribute("role","alert");setText(notice,error&&error.message?error.message:String(error));host.appendChild(notice);host.setAttribute("data-cx-error",notice.textContent);host.dispatchEvent(new CustomEvent("canopyx:error",{detail:{error},bubbles:true}))});Object.freeze(api);activeFlows.set(host,api);host.classList.add("cx-host","cx-flow-host");host.setAttribute("data-canopyx-mounted","true");host.canopyx=api;return api}
|
|
1437
1498
|
const FLOW_API={createFlow};function createTreeGrid(host,options){if(!host||!options||!options.view){throw new Error("CanopyX: createTreeGrid needs a host element and a view document.")}
|
|
@@ -1448,5 +1509,5 @@ watcher.observe(host)}
|
|
|
1448
1509
|
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}
|
|
1449
1510
|
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}}
|
|
1450
1511
|
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}
|
|
1451
|
-
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 flow=view&&view.kind==="flow"&&typeof FLOW_API==="object",matrix=view&&view.type==="matrix"&&typeof MATRIX_API==="object";const change=view&&typeof CHANGE_VIEW_TYPES==="object"&&Object.hasOwn(CHANGE_VIEW_TYPES,view.type)?CHANGE_VIEW_TYPES[view.type]:null;const create=flow?FLOW_API.createFlow:matrix?MATRIX_API.createMatrix:change?change:view&&view.type==="grid"&&typeof createDataGrid==="function"?createDataGrid:createTreeGrid;const api=create(host,Object.assign({},view&&configured.get(view.id),flow?{flow:view}:{view}));created.push(api);api.ready.catch((err)=>{if(view.type!=="grid"&&!matrix&&!change&&activeGrids.get(host)===api&&host.canopyx===api)showError(host,err,api,view)})}catch(err){showError(host,err,null,view)}});return created}
|
|
1452
|
-
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:{},typeof FLOW_API==="object"?FLOW_API:{},typeof MATRIX_API==="object"?MATRIX_API:{},typeof CHANGES_API==="object"?CHANGES_API:{}));const navigation=global.performance&&performance.getEntriesByType?performance.getEntriesByType("navigation")[0]:null;if(document.readyState==="loading"||(document.readyState==="interactive"&&navigation&&navigation.domContentLoadedEventStart===0)){document.addEventListener("DOMContentLoaded",()=>mount(document,true))}else{mount(document,true)}})(typeof window!=="undefined"?window:this);
|
|
1512
|
+
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 flow=view&&view.kind==="flow"&&typeof FLOW_API==="object",matrix=view&&view.type==="matrix"&&typeof MATRIX_API==="object";const change=view&&typeof CHANGE_VIEW_TYPES==="object"&&Object.hasOwn(CHANGE_VIEW_TYPES,view.type)?CHANGE_VIEW_TYPES[view.type]:null;const create=flow?FLOW_API.createFlow:matrix?MATRIX_API.createMatrix:change?change:view&&view.type==="grid"&&typeof createDataGrid==="function"?createDataGrid:createTreeGrid;const api=create(host,Object.assign({},view&&configured.get(view.id),flow?{flow:view}:{view}));created.push(api);api.ready.catch((err)=>{if(view.type!=="grid"&&!matrix&&!change&&activeGrids.get(host)===api&&host.canopyx===api)showError(host,err,api,view)})}catch(err){showError(host,err,null,view)}});mountUi(scope);return created}
|
|
1513
|
+
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:{},typeof FLOW_API==="object"?FLOW_API:{},typeof MATRIX_API==="object"?MATRIX_API:{},typeof CHANGES_API==="object"?CHANGES_API:{},UI_API));const navigation=global.performance&&performance.getEntriesByType?performance.getEntriesByType("navigation")[0]:null;if(document.readyState==="loading"||(document.readyState==="interactive"&&navigation&&navigation.domContentLoadedEventStart===0)){document.addEventListener("DOMContentLoaded",()=>mount(document,true))}else{mount(document,true)}})(typeof window!=="undefined"?window:this);
|