@evotecit/canopyx 0.3.16 → 0.3.20
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 +74 -80
- package/CanopyX.Html/Assets/canopyx-calendar.js +26 -31
- package/CanopyX.Html/Assets/canopyx-cell-range.css +4 -4
- package/CanopyX.Html/Assets/canopyx-cell-range.js +16 -14
- package/CanopyX.Html/Assets/canopyx-changes.css +61 -79
- package/CanopyX.Html/Assets/canopyx-changes.js +18 -15
- package/CanopyX.Html/Assets/canopyx-column-groups.css +5 -1
- package/CanopyX.Html/Assets/canopyx-column-groups.js +15 -9
- 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-detail-rows.css +4 -4
- package/CanopyX.Html/Assets/canopyx-detail-rows.js +1 -1
- package/CanopyX.Html/Assets/canopyx-editing.css +5 -5
- package/CanopyX.Html/Assets/canopyx-editing.js +2 -2
- package/CanopyX.Html/Assets/canopyx-enhance.js +5 -5
- package/CanopyX.Html/Assets/canopyx-export.js +54 -13
- package/CanopyX.Html/Assets/canopyx-facets.css +32 -22
- package/CanopyX.Html/Assets/canopyx-facets.js +19 -15
- package/CanopyX.Html/Assets/canopyx-filter-editor.css +75 -89
- package/CanopyX.Html/Assets/canopyx-filter-editor.js +20 -16
- package/CanopyX.Html/Assets/canopyx-flow-checks.css +3 -3
- package/CanopyX.Html/Assets/canopyx-flow-checks.js +1 -1
- package/CanopyX.Html/Assets/canopyx-flow-command.css +3 -3
- package/CanopyX.Html/Assets/canopyx-flow-command.js +1 -1
- package/CanopyX.Html/Assets/canopyx-flow-dialog.css +4 -4
- package/CanopyX.Html/Assets/canopyx-flow-dialog.js +1 -1
- package/CanopyX.Html/Assets/canopyx-flow-drafts.css +2 -2
- package/CanopyX.Html/Assets/canopyx-flow-drafts.js +1 -1
- package/CanopyX.Html/Assets/canopyx-flow-entry.css +142 -17
- package/CanopyX.Html/Assets/canopyx-flow-entry.js +5 -5
- package/CanopyX.Html/Assets/canopyx-flow-host.css +2 -2
- package/CanopyX.Html/Assets/canopyx-flow-host.js +1 -1
- package/CanopyX.Html/Assets/canopyx-flow-module.mjs +5 -5
- package/CanopyX.Html/Assets/canopyx-flow-pick.css +4 -4
- package/CanopyX.Html/Assets/canopyx-flow-pick.js +1 -1
- package/CanopyX.Html/Assets/canopyx-flow-preview.css +1 -1
- package/CanopyX.Html/Assets/canopyx-flow-preview.js +1 -1
- package/CanopyX.Html/Assets/canopyx-flow-records.css +2 -2
- package/CanopyX.Html/Assets/canopyx-flow-records.js +1 -1
- package/CanopyX.Html/Assets/canopyx-flow-tasks.css +4 -4
- package/CanopyX.Html/Assets/canopyx-flow-tasks.js +1 -1
- package/CanopyX.Html/Assets/canopyx-flow.css +41 -41
- package/CanopyX.Html/Assets/canopyx-flow.js +1 -1
- package/CanopyX.Html/Assets/canopyx-format-editor.css +9 -9
- package/CanopyX.Html/Assets/canopyx-format-editor.js +2 -2
- package/CanopyX.Html/Assets/canopyx-formatting.css +13 -13
- package/CanopyX.Html/Assets/canopyx-formatting.js +12 -11
- package/CanopyX.Html/Assets/canopyx-forms.css +3 -3
- package/CanopyX.Html/Assets/canopyx-forms.js +1 -1
- package/CanopyX.Html/Assets/canopyx-grid-module.mjs +220 -155
- package/CanopyX.Html/Assets/canopyx-grid-options.css +8 -8
- package/CanopyX.Html/Assets/canopyx-grid-options.js +9 -2
- package/CanopyX.Html/Assets/canopyx-grid.css +285 -150
- package/CanopyX.Html/Assets/canopyx-grid.js +220 -155
- package/CanopyX.Html/Assets/canopyx-grouping.css +23 -16
- package/CanopyX.Html/Assets/canopyx-grouping.js +24 -17
- package/CanopyX.Html/Assets/canopyx-highlight.css +10 -10
- package/CanopyX.Html/Assets/canopyx-highlight.js +1 -1
- package/CanopyX.Html/Assets/canopyx-interactive.css +215 -91
- package/CanopyX.Html/Assets/canopyx-interactive.js +47 -52
- 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 +49 -21
- package/CanopyX.Html/Assets/canopyx-layouts.js +36 -14
- package/CanopyX.Html/Assets/canopyx-link.css +3 -3
- package/CanopyX.Html/Assets/canopyx-link.js +4 -4
- package/CanopyX.Html/Assets/canopyx-links.js +2 -2
- package/CanopyX.Html/Assets/canopyx-live.css +3 -5
- package/CanopyX.Html/Assets/canopyx-live.js +3 -3
- package/CanopyX.Html/Assets/canopyx-matrix.css +32 -39
- package/CanopyX.Html/Assets/canopyx-matrix.js +11 -11
- package/CanopyX.Html/Assets/canopyx-module.mjs +47 -52
- package/CanopyX.Html/Assets/canopyx-office-export.js +2936 -0
- package/CanopyX.Html/Assets/canopyx-pager.css +1 -1
- package/CanopyX.Html/Assets/canopyx-pager.js +1 -1
- 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 +63 -0
- package/CanopyX.Html/Assets/canopyx-plan.js +71 -0
- package/CanopyX.Html/Assets/canopyx-presenters.js +2 -2
- package/CanopyX.Html/Assets/canopyx-record-panel.css +51 -45
- package/CanopyX.Html/Assets/canopyx-record-panel.js +30 -22
- package/CanopyX.Html/Assets/canopyx-reporting-module.mjs +262 -197
- package/CanopyX.Html/Assets/canopyx-reporting.css +300 -164
- package/CanopyX.Html/Assets/canopyx-reporting.js +262 -197
- 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.css +19 -19
- package/CanopyX.Html/Assets/canopyx-timeline.js +6 -5
- package/CanopyX.Html/Assets/canopyx-tree-tools.css +47 -46
- 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 +45 -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 +212 -85
- package/CanopyX.Html/Assets/canopyx.js +38 -26
- package/JavaScript/board.d.ts +30 -0
- package/JavaScript/changes.d.ts +4 -1
- package/JavaScript/column-groups.d.ts +24 -4
- package/JavaScript/facets.d.ts +5 -3
- package/JavaScript/grid.d.ts +161 -17
- package/JavaScript/hosts.d.ts +16 -1
- package/JavaScript/index.d.ts +4 -2
- 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 +18 -3
- package/JavaScript/records.d.ts +4 -0
- 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 +76 -187
- package/README.nuget.md +63 -87
- 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)}
|
|
@@ -89,13 +90,13 @@ function trusted(kind,value){if(trustPolicy===undefined){try{trustPolicy=global.
|
|
|
89
90
|
catch(_){trustPolicy=null}}
|
|
90
91
|
return trustPolicy?trustPolicy["create"+kind](value):value}
|
|
91
92
|
function hostHtml(element,html,sanitize,kind){const text=String(html??""),value=sanitize?sanitize(text,{kind}):text;if(value?.nodeType)element.replaceChildren(value);else if(typeof value==="string")element.innerHTML=trusted("HTML",value);else if(global.trustedTypes?.isHTML?.(value))element.innerHTML=value;else throw new Error("CanopyX: invalid sanitizeHtml result.")}
|
|
92
|
-
const STATUS_FALLBACKS={"--hfx-color-status-bad-bg":"light-dark(#f9e4e4,#3c272a)","--hfx-color-status-bad-fg":"light-dark(#b8292a,#f47171)","--hfx-color-status-good-bg":"light-dark(#e2f0e9,#1f362f)","--hfx-color-status-good-fg":"light-dark(#177345,#4cc78a)","--hfx-color-status-info-bg":"light-dark(#e4ecfa,#262f41)","--hfx-color-status-info-fg":"light-dark(#2b5fc0,#74a2f7)","--hfx-color-status-neutral-bg":"light-dark(#eeeff0,#2a2c31)","--hfx-color-status-neutral-fg":"light-dark(#5b6069,#a0a5ae)","--hfx-color-status-warn-bg":"light-dark(#f8efde,#3b3322)","--hfx-color-status-warn-fg":"light-dark(#8c5c05,#eeb640)"};const MODE_ATTRIBUTES=["data-theme","data-cx-theme","data-hfx-mode","data-bs-theme","data-hfx-theme","data-hfx-tokens"];const THEME_ATTRIBUTES=[...MODE_ATTRIBUTES,"data-hfx-style","data-hfx-density","data-cx-fallback-mode","style","class"];function watchTheme(host,signal){const attributes=THEME_ATTRIBUTES;const previous=host.getAttribute("data-cx-fallback-mode");const refresh=()=>{const parent=host.parentElement&&getComputedStyle(host.parentElement);const inherited=parent?.colorScheme;
|
|
93
|
-
if(
|
|
93
|
+
const STATUS_FALLBACKS={"--hfx-color-status-bad-bg":"light-dark(#f9e4e4,#3c272a)","--hfx-color-status-bad-fg":"light-dark(#b8292a,#f47171)","--hfx-color-status-good-bg":"light-dark(#e2f0e9,#1f362f)","--hfx-color-status-good-fg":"light-dark(#177345,#4cc78a)","--hfx-color-status-info-bg":"light-dark(#e4ecfa,#262f41)","--hfx-color-status-info-fg":"light-dark(#2b5fc0,#74a2f7)","--hfx-color-status-neutral-bg":"light-dark(#eeeff0,#2a2c31)","--hfx-color-status-neutral-fg":"light-dark(#5b6069,#a0a5ae)","--hfx-color-status-warn-bg":"light-dark(#f8efde,#3b3322)","--hfx-color-status-warn-fg":"light-dark(#8c5c05,#eeb640)"};const MODE_ATTRIBUTES=["data-theme","data-cx-theme","data-hfx-mode","data-bs-theme","data-hfx-theme","data-hfx-tokens"];const THEME_ATTRIBUTES=[...MODE_ATTRIBUTES,"data-hfx-style","data-hfx-density","data-cx-fallback-mode","style","class"];function watchTheme(host,signal){const attributes=THEME_ATTRIBUTES;const previous=host.getAttribute("data-cx-fallback-mode");const refresh=()=>{const parent=host.parentElement&&getComputedStyle(host.parentElement);const inherited=parent?.colorScheme;const surface=parent&&["--hfx-color-surface-card","--hfx-color-surface"].some(name=>parent.getPropertyValue(name).trim());let mode=null;if(!surface){outer:for(let owner=host;owner;owner=owner.parentElement)for(const attribute of MODE_ATTRIBUTES){const value=owner.getAttribute(attribute);if(value==="light"||value==="dark"){mode=value;break outer}}}
|
|
94
|
+
if(surface||!mode&&inherited&&inherited!=="normal"){if(host.hasAttribute("data-cx-fallback-mode"))host.removeAttribute("data-cx-fallback-mode")}else if(host.getAttribute("data-cx-fallback-mode")!==(mode||"auto"))host.setAttribute("data-cx-fallback-mode",mode||"auto")};const observer=new MutationObserver(refresh);for(let owner=host;owner;owner=owner.parentElement)observer.observe(owner,{attributes:true,attributeFilter:attributes});refresh();const dispose=()=>{observer.disconnect();if(previous===null)host.removeAttribute("data-cx-fallback-mode");else host.setAttribute("data-cx-fallback-mode",previous)};signal?.addEventListener("abort",dispose,{once:true});return dispose}
|
|
94
95
|
function overlayTheme(host,element,inherit=false){for(const name of["data-hfx-style","data-hfx-mode","data-hfx-density"]){const owner=host.closest("["+name+"]");if(owner&&!inherit)element.setAttribute(name,owner.getAttribute(name));else element.removeAttribute(name)}
|
|
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.")}
|
|
@@ -210,8 +214,9 @@ if(kind==="inline"){if(!Array.isArray(view.roots))throw new Error("CanopyX: inli
|
|
|
210
214
|
throw new Error("CanopyX: unknown source kind '"+kind+"'.")}
|
|
211
215
|
async function remoteJson(url,type,timeoutMs,lifetime,signal){const controller=new AbortController();const signals=Array.from(new Set([lifetime,signal].filter(Boolean)));const abort=()=>controller.abort();signals.forEach((item)=>{if(item.aborted)abort();else item.addEventListener("abort",abort,{once:true})});let timedOut=false;const timer=setTimeout(()=>{timedOut=true;abort()},timeoutMs);try{const response=await fetch(url,{headers:{Accept:"application/json"},signal:controller.signal});if(!response.ok){if(response.body)await response.body.cancel().catch(()=>{});throw new Error(response.status+" "+response.statusText)}
|
|
212
216
|
return checkDocument(await response.json(),type)}catch(error){if(timedOut)throw new Error("CanopyX: remote request timed out after "+timeoutMs+" milliseconds.");throw error}finally{abort();clearTimeout(timer);signals.forEach((item)=>item.removeEventListener("abort",abort))}}
|
|
213
|
-
class TreeGrid{constructor(host,options){const view=checkDocument(options.view,"view");
|
|
214
|
-
|
|
217
|
+
class TreeGrid{constructor(host,options){const view=checkDocument(options.view,"view");if(view.recordDrawerModal!==undefined&&typeof view.recordDrawerModal!=="boolean")
|
|
218
|
+
throw new Error("CanopyX: recordDrawerModal must be a Boolean.");requireIdentifier(view.id,"view ID");if(view.columns.some(column=>column.format!==undefined))throw new Error("CanopyX: number formats apply to flat grids only.");this.host=host;this.view=view;this.opts=options;for(const name of["isSelectable","beforeSelect","sanitizeHtml"]){if(options[name]!==undefined&&typeof options[name]!=="function")throw new Error("CanopyX: "+name+" must be a function.")}
|
|
219
|
+
do{this.id="cx" + ++instanceSeq}while(document.getElementById(this.id));this.lifetime=new AbortController();this.source=options.source||sourceFor(view,{signal:this.lifetime.signal,timeoutMs:options.requestTimeoutMs});this.queryAware=this.source.queryAware===true;this.dataLifetime=new AbortController();this.acceptedQuery=Object.freeze({version:1,sort:null,filters:Object.freeze([])});this.datasetRevision=null;this.rowH=view.rowHeight||40;this.pageSize=view.pageSize||100;this.retainedNodeLimit=options.retainedNodeLimit===undefined?1024:options.retainedNodeLimit;if(!validStructuralCount(this.retainedNodeLimit))throw new Error("CanopyX: retainedNodeLimit must be a nonnegative 32-bit integer.");this.inactiveNodeLimit=options.inactiveNodeLimit===undefined?4096:options.inactiveNodeLimit;if(!validStructuralCount(this.inactiveNodeLimit))throw new Error("CanopyX: inactiveNodeLimit must be a nonnegative 32-bit integer.");this.multi=view.selection==="multiple";this.lang=document.documentElement.lang||undefined;this.labels=labelsFor(view.labels,options.labels);this.tools=Object.assign({},view.toolbar,options.toolbar);if(Object.entries(this.tools).some(([name,value])=>!/^(search|filters|columns|views|export|more|matches|layout)$/.test(name) || typeof value !== "boolean"))
|
|
215
220
|
throw new Error("CanopyX: toolbar options are booleans by control name.");this.local=view.timeZone==="local";this.fmt=formatting(this.lang,this.local,this.labels);this.kind="tree";this.plugins=viewPlugins(options,this.kind);const renderers=hostRenderers(options.renderers||{},view.columns,this.plugins);if(!options.formatLink!==!options.parseLink){throw new Error("CanopyX: formatLink and parseLink define one link scheme and are given together.")}
|
|
216
221
|
this.columns=view.columns.map((column,index)=>({...column,hidden:index>0&&!!column.hidden,render:Object.hasOwn(renderers,column.id)?renderers[column.id]:null}));this.columns.forEach(prepareWidth);this.columns.forEach(validateColumnValues);this.keyMap=new Map();this.root={key:null,h:null,parent:null,level:0,idx:0,expanded:true,children:null,total:null,next:null,loading:false,error:null};if(!options.source&&(!view.source||view.source.kind==="inline")){this.root.inlineItems=view.roots||[]}
|
|
217
222
|
this.rows=[];this.tops=[0];this.total=0;this.version=1;this.rendered=new Map();this.pool=[];this.focused=null;this.selected=new Set();this.anchor=null;this.filter=null;this.declaredFilter=null;this.filterExpand=false;this.activeFilters=new Set();this.sortState=null;this.typeBuf="";this.typeAt=0;this.rafId=0;this.iconTemplates=this.buildIcons(view.icons||{});this.initializing=true;this.prepareViewModes();this.build();this.buildToolbar();if(view.deepLink)loadPart(LINKS).catch(()=>{});this.ready=this.modesReady?Promise.all([this.init(),this.modesReady]).then(([result])=>result):this.init()}
|
|
@@ -278,7 +283,7 @@ value.sort===undefined||typeof value!=="object")throw new Error("CanopyX: query
|
|
|
278
283
|
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
284
|
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
285
|
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")}
|
|
286
|
+
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
287
|
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
288
|
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
289
|
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}}
|
|
@@ -406,7 +411,7 @@ if(accepted&&deferred&&revision===this.selectionRevision)
|
|
|
406
411
|
this.notifySelection(w,Array.from(this.selected).map((selected)=>selected.h),revision)}},async retryPage(status){const parent=status.parent;await this.loadStatusPage(parent,status,parent.next||null)},typeAhead(e,i){if(e.key.length!==1||e.ctrlKey||e.metaKey||e.altKey){return false}
|
|
407
412
|
const now=performance.now();const letter=e.key.toLowerCase();this.typeBuf=now-this.typeAt>TYPEAHEAD_MS?letter:this.typeBuf+letter;this.typeAt=now;const n=this.rows.length;const start=this.typeBuf.length===1?i+1:i;for(let k=0;k<n;k++){const w=this.rows[(start+k)%n];if(!w.status&&String(w.h.label).toLowerCase().startsWith(this.typeBuf)){this.setFocus(w);break}}
|
|
408
413
|
return true},announce(message){setText(this.live,message)}});const CELL_CONTROLS="a[href],button,input,select,textarea,summary,[tabindex],[contenteditable]:not([contenteditable=false])," +
|
|
409
|
-
"[role=button],[role=checkbox],[role=radio],[role=link],[role=switch],[role=tab],[role=menuitem],[role=textbox],[role=combobox]";const OWN_ARROWS=".cx-detail-body,textarea,select,[contenteditable]:not([contenteditable=false]),[aria-activedescendant]," +
|
|
414
|
+
"[role=button],[role=checkbox],[role=radio],[role=link],[role=switch],[role=tab],[role=menuitem],[role=textbox],[role=combobox]";const OWN_ARROWS=".cx-detail-body,.cx-phone-value,textarea,select,[contenteditable]:not([contenteditable=false]),[aria-activedescendant]," +
|
|
410
415
|
"[role=textbox],[role=combobox],[role=spinbutton],[role=slider],[role=radio],[role=tab],[role=menuitem],[role=listbox],[role=tree],[role=grid]," +
|
|
411
416
|
"input:not([type=button]):not([type=checkbox]):not([type=submit]):not([type=reset]):not([type=image]):not([type=file]):not([type=color])";function reachable(control){return!control.matches(":disabled")&&!control.closest("[inert],[aria-hidden=true]")&&
|
|
412
417
|
(control.checkVisibility?control.checkVisibility({visibilityProperty:true}):control.getClientRects().length>0)}
|
|
@@ -508,11 +513,10 @@ fill(this.labels.itemsLoaded,{count:count.format(items.length)}));this.stateChan
|
|
|
508
513
|
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
514
|
typeof format.label!=="string"||!format.label.trim()||typeof format.extension!=="string"||!/^[a-z\d]{1,16}$/i.test(format.extension) ||
|
|
510
515
|
format.mime!==undefined&&typeof format.mime!=="string"||
|
|
516
|
+
format.values!==undefined&&!["raw","display"].includes(format.values)||
|
|
511
517
|
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})}
|
|
518
|
+
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
519
|
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
520
|
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
521
|
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
522
|
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,18 +528,20 @@ fill(labels.searchFailed,{error:err&&err.message?err.message:err}))}}};const mov
|
|
|
524
528
|
if(active>=0){list.children[active].setAttribute("aria-selected","false")}
|
|
525
529
|
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
530
|
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
|
-
|
|
531
|
+
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}
|
|
532
|
+
ready().then(value=>{if(!lifetime.signal.aborted&&isOpen())value?.[method]()}).catch(error=>{if(!lifetime.signal.aborted)(anchorElement.closest(".cx-host")||anchorElement)
|
|
533
|
+
.dispatchEvent(new CustomEvent("canopyx:error",{detail:{error},bubbles:true}))})};return{position:()=>invoke("position"),open:()=>invoke("open"),close:()=>flyout?.close(),dispose:()=>lifetime.abort()}}
|
|
534
|
+
const PART_URL=typeof document!=="undefined"&&document.currentScript?.src;const PARTS={"ui-overlays":["8dc12c5deb018c2c","canopyx-ui-overlays.1e39ccf6db0a3df6.js","canopyx-ui-overlays.3668a9a74004ea8e.css"],"ui-menu":["352b1ee391133ff0","canopyx-ui-menu.f9abee36db8f6ad2.js","canopyx-ui-menu.2f3478cc5f5c272f.css"],"ui-picker":["d415103478faf52a","canopyx-ui-picker.13e544bd68aca998.js","canopyx-ui-picker.59c85a1988a287a9.css"],"ui-navigation":["d14b60f4d16a1cf3","canopyx-ui-navigation.02e11de5f457cc64.js","canopyx-ui-navigation.fb8bdcd8dba604c8.css"],"ui-tabs":["96b84956100933e7","canopyx-ui-tabs.f83f7ebb96c86512.js","canopyx-ui-tabs.bb8c7c70a8b5be2c.css"],"ui-stepper":["a31fd5d0b314cdef","canopyx-ui-stepper.dfddf053fc163c73.js","canopyx-ui-stepper.1d4a9db373b8d2f2.css"],"tree-tools":["ef0d06c82135ae2e","canopyx-tree-tools.1c4b9e286dde6065.js","canopyx-tree-tools.350dd0ee46757029.css"],"presenters":["d5f9d61bb730974b","canopyx-presenters.d71f9e12d83969d9.js"],"filter-editor":["b760f978327dbab0","canopyx-filter-editor.4851bd579e7d146c.js","canopyx-filter-editor.2e0cf6850cbd3235.css"],"export":["15abf3205094bda6","canopyx-export.68b90138463329fe.js"],"office-export":["4fb7a4d570390ee9","canopyx-office-export.7e62c385b884e75b.js"],"history":["7d9901d2fe04df72","canopyx-history.9432e724bf14c9ed.js"],"pager":["cc30dd4f6d3feeb0","canopyx-pager.2a6f5da8c93de083.js","canopyx-pager.c1e16027780b7fdd.css"],"enhance":["a3b4be5ec19a43a6","canopyx-enhance.4a189cba27c290de.js"],"data":["bab770170bd2cfdd","canopyx-data.34325d1fd693886e.js"],"forms":["381a3edba481660b","canopyx-forms.fa85aa9e8b62876a.js","canopyx-forms.50a5ddb0fcc98bdf.css"],"flow":["02a915fe174e4710","canopyx-flow.2ec165998fe2950d.js","canopyx-flow.347ecd64183ea3a1.css"],"flow-host":["7169c4283cec8b25","canopyx-flow-host.52ccd1a2b6cd53f7.js","canopyx-flow-host.a7c8d3fc930ad3a9.css"],"flow-checks":["53394e75b7d1b0e2","canopyx-flow-checks.76e58f9d05eaa78f.js","canopyx-flow-checks.88dd9f389b249147.css"],"flow-options":["60450342239ded26","canopyx-flow-options.d31458a54aa4b1e7.js"],"flow-pick":["7e0924c72fdd2f74","canopyx-flow-pick.c2e34da944b0edab.js","canopyx-flow-pick.ce7687598d4703f1.css"],"flow-records":["0b652a9789ce4128","canopyx-flow-records.df8aba0a303019e0.js","canopyx-flow-records.393243243aae9a56.css"],"flow-drafts":["fac75bf905a77d0b","canopyx-flow-drafts.c7db634ab651e9fc.js","canopyx-flow-drafts.2c43cff1ce9be700.css"],"flow-preview":["5e8207eed3801f91","canopyx-flow-preview.3924dc8410f9775d.js","canopyx-flow-preview.069c52c33e87ffb4.css"],"flow-command":["a30245a47f6d4530","canopyx-flow-command.b145a234a6ecc8a2.js","canopyx-flow-command.f98b5333af3a0e17.css"],"flow-tasks":["8a4437f5216ac3ee","canopyx-flow-tasks.569d4c40d317472b.js","canopyx-flow-tasks.54857a9f2732048a.css"],"flow-dialog":["8c45e0a8bb6a8ab9","canopyx-flow-dialog.cb4e85bcb7383fe2.js","canopyx-flow-dialog.58bd5d446415e01a.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":["002a6d917bb4d638","canopyx-matrix.17ae2d0e03ad1364.js","canopyx-matrix.59fc6920ae000d49.css"],"changes":["7d9a526aa07a8838","canopyx-changes.75f1a61cbba98a4f.js","canopyx-changes.5f0ae1710b8b081c.css"],"editing":["88e4ca705a7eb0c3","canopyx-editing.6a6df811fb4dde54.js","canopyx-editing.4816f6be6f89d794.css"],"column-groups":["91abd99e8ff95b58","canopyx-column-groups.4c71d23375de727c.js","canopyx-column-groups.4e4131c1e2df40b1.css"],"cell-range":["cb29468954702795","canopyx-cell-range.d4f99a024db5d0f8.js","canopyx-cell-range.c790b8f140b1243b.css"],"links":["c6579c9401f0971d","canopyx-links.b25760079f57e6e1.js"],"search":["521eebd6e1bbbd35","canopyx-search.14c69f4551c69f23.js"],"formatting":["0feff4e3424599e9","canopyx-formatting.94536547a1d35e9b.js","canopyx-formatting.22b2400c566e25d6.css"],"format-editor":["97085e181fbc1cf7","canopyx-format-editor.a6bc57c48173bfc0.js","canopyx-format-editor.d5aaa8e873d92695.css"],"link":["7485f5c9875e9f9b","canopyx-link.9066770aa822fe0f.js","canopyx-link.997fefec6b85f0c5.css"],"detail-rows":["1892f1fd9d035c0f","canopyx-detail-rows.8be98dbc5721170a.js","canopyx-detail-rows.275076ec8bd9144a.css"],"calculations":["f902dc5856fc7bdd","canopyx-calculations.97e438237ba43f99.js","canopyx-calculations.50432e524dd7d8d9.css"],"pivot":["577443c67c7ed719","canopyx-pivot.3abc630122030933.js","canopyx-pivot.5670e62c0bbf114f.css"],"values":["0d7006b0f7bfca10","canopyx-values.ad4c56fcbcd89cbb.js","canopyx-values.446654b98c62ef1d.css"],"buckets":["cc69c8336c15df83","canopyx-buckets.1f59613f13ee292c.js"],"grouping":["c82705991ff01a4c","canopyx-grouping.239f13b0785a2e30.js","canopyx-grouping.a91801c602d752b3.css"],"highlight":["5ebf0042011ba4fd","canopyx-highlight.ff11a0ca8e30102b.js","canopyx-highlight.9c26291cbcd0962b.css"],"layouts":["90267e35e7edf3a4","canopyx-layouts.058b8a396f909a79.js","canopyx-layouts.ca17f42babc4192a.css"],"facets":["e78c2340a96fa802","canopyx-facets.74b2e520a3c04580.js","canopyx-facets.e2970dd7cf0c3da6.css"],"live":["78791b002a111843","canopyx-live.878dc16d683abb72.js","canopyx-live.9881627f352f59da.css"],"record-panel":["cb6bcd1baf644a51","canopyx-record-panel.557c676822e8624f.js","canopyx-record-panel.174846a640de979f.css"],"calendar":["e11aad6b723ddf23","canopyx-calendar.79518c25ddf28dd7.js","canopyx-calendar.47490d57a6e6c1dc.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":["cb9b9dd33154172c","canopyx-timeline.a3dea165914fa7fd.js","canopyx-timeline.44c3156c218531e2.css"],"grid-options":["ee53e6f124b99d5a","canopyx-grid-options.9f8604806c93bf19.js","canopyx-grid-options.e85648a66d70d5b5.css"],"board":["dd02be00df77f53a","canopyx-board.81de43b6e0f4aec3.js","canopyx-board.b62cc3a0348e758a.css"],"plan":["db07c2eb53541d49","canopyx-plan.f3bf394516b627b7.js","canopyx-plan.743201d95414f399.css"],"path":["c6fa2dbab6a0dbaa","canopyx-path.97c3ee2b076b2db6.js","canopyx-path.efba21adc0bfc372.css"],"stack":["e29298edc07edb6d","canopyx-stack.82a5a0c6383519dc.js","canopyx-stack.b867aa57edf9002c.css"],"time-slider":["5516099a165cc345","canopyx-time-slider.ed59a275671c729f.js","canopyx-time-slider.dfc55326f03e1d4a.css"],"view-intents":["dbd26360f2b90a49","canopyx-view-intents.3adcb56dffca0650.js","canopyx-view-intents.29a5c2de68ce3ba0.css"],"compare":["46245b96483e9c30","canopyx-compare.2a8ddc0ce67e903f.js","canopyx-compare.da0a2dff321e782d.css"]},PART_CODE=new Map(),SHARED_PARTS={},partName=name=>typeof name==="string"&&/^[a-z][a-z\d-]{0,31}$/.test(name);
|
|
529
535
|
function registerPart(name,part){if(!partName(name)||!part||typeof part.version!=="string"||typeof part.install!=="function"||
|
|
530
536
|
part.requires!==undefined&&!(Array.isArray(part.requires)&&part.requires.every(partName)))throw new Error("CanopyX: invalid part.");PART_CODE.set(name,part)}
|
|
531
537
|
function definePart(View,name,label,context){const parts=View.prototype.parts||={};return parts[name]={name,label,context,parts}}
|
|
532
|
-
function loadPart(part){return part.ready||=Promise.resolve().then(async()=>{const[version,scriptFile,styleFile]=Object.hasOwn(PARTS,part.name)?PARTS[part.name]:[];part.version=version;if(!version)throw new Error("CanopyX:
|
|
538
|
+
function loadPart(part){return part.ready||=Promise.resolve().then(async()=>{const[version,scriptFile,styleFile]=Object.hasOwn(PARTS,part.name)?PARTS[part.name]:[];part.version=version;if(!version)throw new Error("CanopyX: no "+part.label+".");if(PART_CODE.get(part.name)?.version!==part.version||part.styleReady===null){const find=type=>[...document.querySelectorAll('script[type="application/x-canopyx-'+type+'"][data-cx-version="'+part.version+'"]')].find(item=>!pageNonce||item.nonce===pageNonce);const payload=find(part.name),script=h("script"),embeddedStyle=styleFile&&find(part.name+"-style");if(payload){if(embeddedStyle){const style=h("style");style.nonce=payload.nonce;setText(style,embeddedStyle.textContent);document.head.appendChild(style)}
|
|
533
539
|
script.textContent=trusted("Script",payload.textContent);script.nonce=payload.nonce;document.head.appendChild(script);script.remove()}else{const base=global.CanopyXFilterEditorBase,runtime=PART_URL||typeof base==="string"&&base||document.baseURI,hashed=PART_URL&&/\.[a-f\d]{16}\.js(?:[?#]|$)/.test(PART_URL);
|
|
534
540
|
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
541
|
.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
542
|
.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,
|
|
543
|
+
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})}
|
|
544
|
+
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
545
|
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
546
|
throw new Error("CanopyX: invalid state column.");seen.add(c.id);return{id:c.id,width:c.width,hidden:c.hidden}}
|
|
541
547
|
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 +592,12 @@ w.content=nodeContent(w.h)});this.inlineOrderVersion=(this.inlineOrderVersion||0
|
|
|
586
592
|
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
593
|
const navigation=navigationScope(this,this.dataLifetime.signal);let doc;try{doc=await navigation.wait(this.source.counts(wanted,this.sourceRequest(this.dataLifetime.signal)))}
|
|
588
594
|
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}});
|
|
595
|
+
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(){}
|
|
596
|
+
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}
|
|
597
|
+
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}
|
|
598
|
+
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))}}
|
|
599
|
+
return mounted}
|
|
600
|
+
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
601
|
}
|
|
591
602
|
this.linkChanged=this.dataLifetime.signal;return this.ready.then(()=>undefined,()=>undefined)}
|
|
592
603
|
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 +618,7 @@ DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{linkFilteredBy:"Fi
|
|
|
607
618
|
linkMethods(TreeGrid);function linkGridPart(View){(Object.hasOwn(View.prototype,"parts")?View.prototype.parts:View.prototype.parts={}).link=LINK_PART}
|
|
608
619
|
function linkReady(grid,ready){return Promise.all([ready,grid.linkStart()]).then(([result])=>result)}
|
|
609
620
|
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)}
|
|
621
|
+
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
622
|
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
623
|
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
624
|
if(!options||typeof options!=="object"||Array.isArray(options)||Object.keys(options).some(key=>!HIGHLIGHT_KEYS.includes(key))||
|
|
@@ -629,16 +640,21 @@ function densityRowHeight(view,density){const shared=sharedDensity(view.host);if
|
|
|
629
640
|
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
641
|
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
642
|
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()}});
|
|
643
|
+
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))
|
|
644
|
+
.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:'""'}
|
|
645
|
+
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
646
|
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
647
|
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
648
|
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
649
|
function gridFreeze(value){if(value&&typeof value==="object"&&!Object.isFrozen(value)){Object.values(value).forEach(gridFreeze);Object.freeze(value)}
|
|
638
650
|
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)}
|
|
651
|
+
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
652
|
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)}
|
|
653
|
+
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)}
|
|
654
|
+
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})}
|
|
655
|
+
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]})}
|
|
656
|
+
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()}
|
|
657
|
+
return gridFreeze({kind:input.kind,timeZone,firstWeekday})}
|
|
642
658
|
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
659
|
function gridColumnOptions(column,columns){if(column.className!=null&&!gridClassName(column.className)||
|
|
644
660
|
column.alignment!=null&&!["start","center","end"].includes(column.alignment)||
|
|
@@ -660,31 +676,41 @@ for(const key of keys)if(defaults[key]===undefined&&rule[key]!=null)defaults[key
|
|
|
660
676
|
function gridOptionsUsed(view,plugins=[]){return!!(view.footers?.length||view.columns.some(column=>column.orderData||column.sortComparator)||
|
|
661
677
|
plugins.some(plugin=>plugin.filterOperators||plugin.sortComparators))}
|
|
662
678
|
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)
|
|
679
|
+
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"||
|
|
680
|
+
!(Object.hasOwn(GRID_FILTER_LABELS,filter.operator)||!bucket&&/^[a-z][a-zA-Z\d]{0,31}$/.test(filter.operator)) ||
|
|
665
681
|
!Array.isArray(values))
|
|
666
682
|
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
683
|
throw new Error("CanopyX: invalid grid predicate arity.");if(["contains","startsWith"].includes(filter.operator)&&(column.kind!=="text"||values.some(value=>typeof value!=="string"))||
|
|
668
684
|
["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)}
|
|
685
|
+
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
686
|
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
687
|
!group.filters.length&&!group.groups.length)
|
|
672
688
|
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
689
|
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
690
|
group.groups.every(child=>gridMatchesGroup(row,child,columns,matches));return group.filters.some(filter=>matches(row,filter,columns.get(filter.column)))||
|
|
675
691
|
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);
|
|
692
|
+
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
693
|
}
|
|
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);
|
|
694
|
+
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)}
|
|
695
|
+
return foldText(text).includes(term)}))}
|
|
696
|
+
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)||
|
|
697
|
+
tuple.columns.length<1||tuple.columns.length>6||!Array.isArray(tuple.values)||
|
|
698
|
+
tuple.values.length<1||tuple.values.length>200||tuple.exclude!==undefined&&typeof tuple.exclude!=="boolean")
|
|
699
|
+
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)
|
|
700
|
+
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)}
|
|
701
|
+
return value})});return gridQueryRecord(tuple,{columns:fields,values,exclude:tuple.exclude??false},strict)})}
|
|
702
|
+
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
703
|
function gridQueryStart(query,patch){return{...query,...patch,offset:0,cursor:undefined,revision:undefined}}
|
|
680
704
|
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
705
|
Object.keys(format).some(key=>!["minimumFractionDigits","maximumFractionDigits","prefix","suffix"].includes(key))||
|
|
682
706
|
[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");
|
|
684
|
-
throw new Error("CanopyX:
|
|
685
|
-
throw new Error("CanopyX:
|
|
686
|
-
throw new Error("CanopyX:
|
|
687
|
-
throw new Error("CanopyX:
|
|
707
|
+
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)
|
|
708
|
+
throw new Error("CanopyX: choose one grid presentation: board, path, stack or calendar (with optional plan).");view=gridColumnDefaults(view);if(view.selectOnClick!==undefined&&typeof view.selectOnClick!=="boolean")throw new Error("CanopyX: selectOnClick is a boolean.");if(view.allowRegexSearch!==undefined&&typeof view.allowRegexSearch!=="boolean")throw new Error("CanopyX: allowRegexSearch is a boolean.");const groups=[view.columnBands,view.columnGroups].filter(value=>value!==undefined);if(groups.length>1||groups.some(value=>!Array.isArray(value)))
|
|
709
|
+
throw new Error("CanopyX: choose columnBands or columnGroups as an array.");if(view.rowIdentity!==undefined&&!["stableKey","ordinal"].includes(view.rowIdentity))
|
|
710
|
+
throw new Error("CanopyX: rowIdentity must be stableKey or ordinal.");if(view.rowIdentity==="ordinal"&&(view.live||view.record?.deepLink))
|
|
711
|
+
throw new Error("CanopyX: rowIdentity must be stableKey for live updates or deep links.");if(typeof view.label!=="string"||!view.label.trim()||!Array.isArray(view.columns)||!view.columns.length)
|
|
712
|
+
throw new Error("CanopyX: a grid needs a label and columns.");if(!view.source||typeof view.source!=="object"||!["inline","host"].includes(view.source.kind))
|
|
713
|
+
throw new Error("CanopyX: a grid needs source.kind inline or host.");if(view.reporting!==undefined){if(!GRID_REPORTING.validate)throw new Error("CanopyX: this view needs the reporting runtime.");GRID_REPORTING.validate(view)}
|
|
688
714
|
const ids=new Set();for(const column of view.columns){requireIdentifier(column&&column.id,"grid column ID");if(ids.has(column.id)||typeof column.title!=="string"||!["text","number","datetime"].includes(column.kind)||
|
|
689
715
|
column.headerDescription!==undefined&&typeof column.headerDescription!=="string"||
|
|
690
716
|
column.width!==undefined&&(!Number.isInteger(column.width)||column.width<40)||
|
|
@@ -692,45 +718,60 @@ column.width!==undefined&&(!Number.isInteger(column.width)||column.width<40)||
|
|
|
692
718
|
column.flex!==undefined&&!(typeof column.flex==="number"&&column.flex>=0&&column.flex<=100)||column.autoSize!==undefined&&!["content","header","none"].includes(column.autoSize)||
|
|
693
719
|
column.pinned!==undefined&&![null,"left","right"].includes(column.pinned)||column.priority!==undefined&&!gridPriority(column.priority)||
|
|
694
720
|
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);
|
|
721
|
+
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))
|
|
722
|
+
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
723
|
if(column.aggregatable!==undefined&&(typeof column.aggregatable!=="boolean"||column.aggregatable&&column.kind!=="number")||
|
|
697
724
|
column.section!==undefined&&(typeof column.section!=="string"||!column.section.trim()||column.section.length>256))
|
|
698
725
|
throw new Error("CanopyX: invalid column aggregate or drawer section.")}
|
|
699
726
|
if(view.columns.every(column=>column.hidden)||view.columns.filter(column=>column.rowHeader).length>1)
|
|
700
727
|
throw new Error("CanopyX: a grid needs a visible column and one row header at most.");if(view.density!==undefined&&!["compact","default","comfortable"].includes(view.density)||
|
|
701
|
-
["zebra","statusBar","autoHeight","rowAccents"].some(name=>view[name]!==undefined&&typeof view[name]!=="boolean")||
|
|
728
|
+
["zebra","statusBar","autoHeight","rowAccents","recordDrawerModal"].some(name=>view[name]!==undefined&&typeof view[name]!=="boolean")||
|
|
702
729
|
view.rowAction!==undefined&&!["none","details","link"].includes(view.rowAction)||
|
|
703
730
|
view.noun!==undefined&&(typeof view.noun!=="string"||!view.noun.trim()||view.noun.length>256)||
|
|
704
731
|
view.emptyReason!==undefined&&(typeof view.emptyReason!=="string"||view.emptyReason.length>4096))throw new Error("CanopyX: invalid grid appearance or state text.");validateGridHighlights(view);if(view.searchDelayMs!==undefined&&(!Number.isInteger(view.searchDelayMs)||view.searchDelayMs<0||view.searchDelayMs>5000))
|
|
705
732
|
throw new Error("CanopyX: search delay must be an integer from 0 to 5000 milliseconds.");if(view.searchColumns!==undefined&&(!Array.isArray(view.searchColumns)||!view.searchColumns.length||
|
|
706
733
|
new Set(view.searchColumns).size!==view.searchColumns.length||view.searchColumns.some(id=>!ids.has(id))))
|
|
707
|
-
throw new Error("CanopyX: search columns must identify unique declared columns.");if(view.exportFormats!==undefined&&!exportFormatIds(view.exportFormats))throw new Error("CanopyX: exportFormats lists unique format IDs.");if(view.
|
|
708
|
-
|
|
709
|
-
view.
|
|
710
|
-
!Number.isInteger(view.
|
|
711
|
-
|
|
734
|
+
throw new Error("CanopyX: search columns must identify unique declared columns.");if(view.exportFormats!==undefined&&!exportFormatIds(view.exportFormats))throw new Error("CanopyX: exportFormats lists unique format IDs.");if(!Number.isInteger(view.pageSize)||view.pageSize<1||view.pageSize>10000)
|
|
735
|
+
throw new Error("CanopyX: pageSize must be 1 to 10000.");if(!["none","single","multiple"].includes(view.selection))
|
|
736
|
+
throw new Error("CanopyX: selection must be none, single or multiple.");if(view.autoRowHeight!==undefined&&typeof view.autoRowHeight!=="boolean")
|
|
737
|
+
throw new Error("CanopyX: autoRowHeight is a boolean.");if(view.rowHeight!==undefined&&view.rowHeight!=="auto"&&(!Number.isInteger(view.rowHeight)||view.rowHeight<20||view.rowHeight>64))
|
|
738
|
+
throw new Error("CanopyX: rowHeight must be auto or 20 to 64.");if(view.estimatedRowHeight!==undefined&&(!Number.isInteger(view.estimatedRowHeight)||view.estimatedRowHeight<20||view.estimatedRowHeight>64))
|
|
739
|
+
throw new Error("CanopyX: estimatedRowHeight must be 20 to 64.");if(view.calculatedColumnsEditable!==undefined&&(typeof view.calculatedColumnsEditable!=="boolean"||view.calculatedColumnsEditable&&view.source.kind!=="inline"))
|
|
740
|
+
throw new Error("CanopyX: the calculated-column editor requires an inline grid.");if(view.source.navigation!==undefined&&!["offset","cursor"].includes(view.source.navigation))
|
|
712
741
|
throw new Error("CanopyX: invalid grid source navigation.");if(view.source.navigation==="cursor"&&(view.source.kind!=="host"||view.presentation!=="paged"))
|
|
713
742
|
throw new Error("CanopyX: cursors need a hosted paged grid.");if(view.presentation!==undefined&&!["continuous","paged"].includes(view.presentation)||
|
|
714
743
|
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
|
-
|
|
744
|
+
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.")}
|
|
745
|
+
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)}}
|
|
746
|
+
function gridQuery(view,input,strict){input=input||{};if(Array.isArray(input.tupleFilters)&&!input.tupleFilters.length){input={...input};delete input.tupleFilters}
|
|
747
|
+
const cursor=view.source.navigation==="cursor",type=cursor?"gridCursorQuery":"gridQuery";if(typeof input!=="object"||Array.isArray(input)||
|
|
717
748
|
input.protocol!==undefined&&input.protocol!==PROTOCOL||input.type!==undefined&&input.type!==type||
|
|
718
749
|
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
750
|
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
751
|
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.");
|
|
752
|
+
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)}
|
|
753
|
+
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
754
|
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
755
|
!["asc","desc"].includes(sort.direction))
|
|
724
756
|
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
757
|
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)}
|
|
758
|
+
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
759
|
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
760
|
page.matched>page.total||page.offset>page.matched||!Array.isArray(page.items)||
|
|
729
761
|
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
762
|
function checkedGridPage(page,view,query,accepted){if(query.revision!==undefined&&page.revision!==query.revision||accepted&&
|
|
731
763
|
(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
|
|
764
|
+
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}}
|
|
765
|
+
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}}
|
|
766
|
+
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{}
|
|
767
|
+
delete kept.sort;try{validateGridCell(kept,column);return kept}catch{}}
|
|
768
|
+
return text===undefined?value:{value,text}}
|
|
769
|
+
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)||
|
|
770
|
+
!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) => {
|
|
771
|
+
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.");
|
|
772
|
+
}}
|
|
733
773
|
function gridOrder(left,right){return left===right?0:left<right?-1:1}
|
|
774
|
+
function gridInlineRevision(view){return GRID_CALCULATIONS.revision?.(view)||"inline"}
|
|
734
775
|
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
776
|
return id.length>0}
|
|
736
777
|
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 +779,13 @@ function gridSortScalar(cell,column){return!column.valueOrder?.length&&isCellObj
|
|
|
738
779
|
function gridCompare(left,right,column,direction){return gridKeyCompare(gridSortScalar(left.cells[column.id],column),gridSortScalar(right.cells[column.id],column),column,direction)}
|
|
739
780
|
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
781
|
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}
|
|
782
|
+
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)}
|
|
783
|
+
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)&&
|
|
784
|
+
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
785
|
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
786
|
return gridIdOrder(rows[a].id,rows[b].id)}).map(index=>rows[index])}
|
|
746
787
|
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:
|
|
788
|
+
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
789
|
return ordered.findIndex(row=>row.id===id)},row(id){return view.rows.find(row=>row.id===id)||null}}}
|
|
749
790
|
function gridCursor(cursor){requireIdentifier(cursor,"grid cursor");if(cursor.length>4096)throw new Error("CanopyX: cursor exceeds 4096 code units.")}
|
|
750
791
|
function validateGridCursorPage(page,view,query,accepted){checkDocument(page,"gridCursorPage");requireIdentifier(page.revision,"grid revision");if(page.view!==view.id||!validStructuralCount(page.offset)||
|
|
@@ -756,7 +797,7 @@ page.matched!==null&&next!==(end<page.matched)||page.total!==null&&next&&end>=pa
|
|
|
756
797
|
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
798
|
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
799
|
typeof scalar==="boolean"?"b:"+scalar:"n:"+scalar}
|
|
759
|
-
function validateGridFacetPage(input,view,request,snapshot){checkDocument(input,"gridFacetPage");const
|
|
800
|
+
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
801
|
input.search!==request.search||input.includeCurrentFilter!==request.includeCurrentFilter||
|
|
761
802
|
!Number.isInteger(input.matched)||input.matched<0||input.matched>MAX_PROTOCOL_INTEGER||
|
|
762
803
|
!Number.isInteger(input.totalValues)||input.totalValues<0||input.totalValues>input.matched||
|
|
@@ -767,10 +808,10 @@ gridResultShape(gridColumnProjection(request.query,request.column))!==gridResult
|
|
|
767
808
|
(widened?input.matched<snapshot.matched:input.matched!==snapshot.matched))
|
|
768
809
|
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
810
|
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})}
|
|
811
|
+
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
812
|
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
|
-
throw new Error("CanopyX:
|
|
773
|
-
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"))
|
|
813
|
+
throw new Error("CanopyX: rowIdentity must be stableKey for live updates or deep links.");if(rows)this.view.rows=owned?rows:JSON.parse(JSON.stringify(rows));if(rowData){this.view.rowData=owned?rowData:structuredClone(rowData);this.view.rows=[]}
|
|
814
|
+
this.selectOnClick=this.view.selectOnClick===true;this.host=host;this.opts=options;do{this.id="cx" + ++instanceSeq}while(document.getElementById(this.id));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"))
|
|
774
815
|
throw new Error("CanopyX: source navigation must match the grid definition.");this.pageSize=this.view.pageSize;this.authoredAutoRows=this.view.rowHeight==="auto"||!!this.view.autoRowHeight||
|
|
775
816
|
this.view.autoRowHeight!==false&&this.view.columns.some(column=>column.wrap);this.autoRows=this.authoredAutoRows;this.rowH=this.view.rowHeight==="auto"?this.view.estimatedRowHeight||(this.view.density==="compact"?32:40):this.view.rowHeight;this.rowHeights=new Map();this.paged=this.view.presentation==="paged";this.pageLength=this.view.pageLength||50;this.pageOffset=0;this.searchDelayMs=options.searchDelayMs===undefined?this.view.searchDelayMs===undefined?
|
|
776
817
|
this.view.source.kind==="inline"?0:300:this.view.searchDelayMs:options.searchDelayMs;if(!Number.isInteger(this.searchDelayMs)||this.searchDelayMs<0||this.searchDelayMs>5000)
|
|
@@ -788,10 +829,12 @@ async setQuery(input,paging){if(this.destroyed)throw new DOMException("Grid was
|
|
|
788
829
|
const same=this.acceptedQuery&&gridResultShape(initial)===gridResultShape(this.acceptedQuery)&&initial.revision===this.acceptedQuery.revision;let query=this.paged&&!this.cursor?gridQuery(this.view,{...initial,offset:same?Math.min(desired,Math.max(0,Math.ceil(this.rowCount()/ length) - 1) * length) : 0 }) : initial;
|
|
789
830
|
const epoch=++this.queryEpoch;clearTimeout(this.searchTimer);this.searchTimer=null;this.dataLifetime.abort();this.dataLifetime=new AbortController();const lifetime=this.dataLifetime;this.pendingPages.clear();this.pendingQuery=query;this.pendingPaging=paging?{...paging,pageLength:length}:null;this.setNotice(this.labels.loading,false);this.grid.setAttribute("aria-busy","true");this.syncPager();try{let page=(this.cursor?validateGridCursorPage:validateGridPage)(await this.requestPage(query,lifetime.signal),this.view,query,same?this.snapshot:null);if(this.cursor&&paging&&paging.direction)this.validateCursorTransition(page,paging);if(this.paged&&!this.cursor&&!same&&desired){gridOperationActive(this,lifetime.signal);const offset=Math.min(desired,Math.max(0,Math.ceil(page.matched / length) - 1) * length);
|
|
790
831
|
if(offset){const snapshot={revision:page.revision,total:page.total,matched:page.matched};query=gridQuery(this.view,{...query,offset,revision:page.revision});page=validateGridPage(await this.requestPage(query,lifetime.signal),this.view,query,snapshot)}}
|
|
791
|
-
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}
|
|
832
|
+
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;if(!same||this.snapshot?.revision!==page.revision)this.selectionAnchor=null;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
833
|
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
834
|
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;
|
|
835
|
+
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?
|
|
836
|
+
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("")}
|
|
837
|
+
this.syncQuerySummary();this.syncPager();this.fitHeight();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();this.fitHeight();if(!cancelled)this.emit("error",{error})}
|
|
795
838
|
throw error}}
|
|
796
839
|
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
840
|
sort(sorts){return this.setQuery(gridQueryStart(this.queryForChange(true),{sort:sorts}))}
|
|
@@ -804,12 +847,12 @@ indexOf(row){if(!row||this.paged&&(row._ri<this.pageOffset||row._ri>=this.pageOf
|
|
|
804
847
|
rowItem(index){if(index<0||index>=this.rowCount()||this.paged&&(index<this.pageOffset||index>=this.pageOffset+this.pageLength))return null;if(this.rowCache.has(index))return this.rowCache.get(index);if(!this.placeholders.has(index))this.placeholders.set(index,{_ri:index,hpx:this.rowH,status:"loading",h:{cells:{}}});const row=this.placeholders.get(index),page=this.pages.get(Math.floor(index / this.pageSize) * this.pageSize);
|
|
805
848
|
row.status=page&&page.error?"error":"loading";return row}
|
|
806
849
|
render(){if(this.destroyed||!this.sized)return;let measured=false;if(this.autoRows)this.syncRowOffsets();for(let pass=0;;pass++){this.renderPass();if(!this.autoRows||!this.measureGridRows())break;measured=true;if(pass===3){this.scheduleRender();break}}
|
|
807
|
-
for(const[index,row]of this.placeholders)if(!this.rendered.has(row)&&this.focused!==row)this.placeholders.delete(index);this.loadWindow();const origin=this.focusOrigin;if(origin&&this.hasGridSize()){this.focusOrigin=null;if(document.activeElement===origin)(this.rendered.get(this.focused)||this.grid).focus({preventScroll:true})}
|
|
850
|
+
this.syncGridPhoneValues?.();for(const[index,row]of this.placeholders)if(!this.rendered.has(row)&&this.focused!==row)this.placeholders.delete(index);this.loadWindow();const origin=this.focusOrigin;if(origin&&this.hasGridSize()){this.focusOrigin=null;if(document.activeElement===origin)(this.rendered.get(this.focused)||this.grid).focus({preventScroll:true})}
|
|
808
851
|
return measured}
|
|
809
852
|
observeDetail(){}
|
|
810
853
|
trimInactiveCache(){this.trimPages()}
|
|
811
854
|
clearSnapshotSelection(){const changed=this.selected.size||this.querySelection;this.selectionController?.abort();this.selectionAnchor=null;this.querySelection=null;this.selectionExcluded=null;this.selected.clear();if(changed){this.version++;this.scheduleRender();this.emit("select",{ids:[]});this.syncGridChrome()}}
|
|
812
|
-
setSelection(ids){if(this.destroyed)return;if(!Array.isArray(ids))throw new Error("CanopyX: selection must be an array of row IDs.");ids.forEach(id=>requireIdentifier(id,"selection ID"));const next=new Set(ids);if(this.view.selection==="none"&&next.size||!this.multi&&next.size>1)throw new Error("CanopyX: selection exceeds the view's mode.");this.selected=next;this.version++;this.scheduleRender();this.emit("select",{ids:[...next]});this.syncGridChrome();this.stateChanged()}
|
|
855
|
+
setSelection(ids){if(this.destroyed)return;if(!Array.isArray(ids))throw new Error("CanopyX: selection must be an array of row IDs.");ids.forEach(id=>requireIdentifier(id,"selection ID"));const next=new Set(ids);if(this.view.selection==="none"&&next.size||!this.multi&&next.size>1)throw new Error("CanopyX: selection exceeds the view's mode.");this.selectionController?.abort();this.selectionAnchor=null;this.selected=next;this.version++;this.scheduleRender();this.emit("select",{ids:[...next]});this.syncGridChrome();this.stateChanged()}
|
|
813
856
|
setColumnVisible(id,visible){const column=this.columns.find(item=>item.id===id);if(!column||typeof visible!=="boolean")throw new Error("CanopyX: visibility needs a declared column and boolean.");if(!visible&&this.columns.filter(item=>!item.hidden).length===1&&!column.hidden)throw new Error("CanopyX: keep at least one column visible.");column.hidden=!visible;this.buildHeader();this.version++;this.scheduleRender();this.stateChanged();this.syncColumnControls()}
|
|
814
857
|
destroy(){if(this.destroyed)return;this.writeStoredState(this.getState());this.destroyed=true;this.queryEpoch++;this.lifetime.abort();this.dataLifetime.abort();this.focusOrigin=null;for(const row of[...this.rendered.values(),...this.pool])disposeRowHostCells(row);if(this.rafId)cancelAnimationFrame(this.rafId);clearTimeout(this.searchTimer);this.searchTimer=null;clearTimeout(this.stateTimer);if(this.disposeControls)this.disposeControls();if(this.cancelResize)this.cancelResize();this.resizeObserver.disconnect();this.detailObserver?.disconnect();this.rowHeights?.clear();this.rowOffsets=null;this.restoreHeight();this.host.classList.remove("cx-data-grid","cx-chrome","cx-phone","cx-has-selection","cx-scroll-left","cx-scroll-right","cx-grid-awaiting","cx-pins");this.grid.removeEventListener("wheel",this.onScaledWheel);this.grid.removeEventListener("touchstart",this.onScaledTouchStart);this.grid.removeEventListener("touchmove",this.onScaledTouchMove);this.grid.removeEventListener("touchend",this.onScaledTouchEnd);this.grid.removeEventListener("touchcancel",this.onScaledTouchEnd);this.pages.clear();this.rowCache.clear();this.placeholders.clear();this.pendingPages.clear();this.rendered.clear();this.pool.length=0;this.toolbar.remove();this.grid.remove();this.notice.remove();this.live.remove();this.querySummary?.remove();if(this.pager)this.pager.remove();this.statusBar?.remove();this.groupBar?.remove();this.statusAggregateController?.abort();this.host.removeAttribute("data-cx-ready");this.host.removeAttribute("data-canopyx-mounted")}}
|
|
815
858
|
Object.assign(DataGrid.prototype,ROW_SCROLLING,ROW_DOM_ORDER,ROW_VIEWPORT,ROW_HELPERS);Object.assign(DataGrid.prototype,{requestPage(query,signal){return this.runGridHook(signal=>this.source.query(query,{signal}),signal)},storePage(page){const rows=page.items.map((item,i)=>({key:item.id,h:item,_ri:page.offset+i,hpx:this.rowH}));const incoming=new Map(rows.map(row=>[row.key,row._ri]));for(const cached of this.rowCache.values())if(incoming.has(cached.key)&&incoming.get(cached.key)!==cached._ri)
|
|
@@ -824,7 +867,7 @@ if(!this.rowCache.has(index))offsets.add(Math.floor(index / this.pageSize) * thi
|
|
|
824
867
|
if(this.focused?.status&&this.focused._ri>=0)offsets.add(Math.floor(this.focused._ri / this.pageSize) * this.pageSize);
|
|
825
868
|
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
869
|
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])}
|
|
870
|
+
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
871
|
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
872
|
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
873
|
pageCount:this.paged?Math.ceil(count / this.pageLength) : count ? 1 : 0 } });
|
|
@@ -850,14 +893,16 @@ if(this.pendingNavigation!==navigation)return;if(this.destroyed||this.pendingQue
|
|
|
850
893
|
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
894
|
!this.destroyed&&!this.dataLifetime.signal.aborted&&epoch===this.queryEpoch&&!this.pendingQuery&&
|
|
852
895
|
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))
|
|
896
|
+
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
897
|
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
898
|
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)}
|
|
899
|
+
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}
|
|
900
|
+
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
901
|
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)},
|
|
902
|
+
else this.scheduleRender()};cancelAnimationFrame(resizeFrame);resizeFrame=requestAnimationFrame(resize)});this.resizeObserver.observe(grid);this.resizeObserver.observe(this.head);this.resizeObserver.observe(host)},minimumGridHeight(rows=true){if(rows&&this.toolbar.hidden)return 0;const host=getComputedStyle(this.host);let count=0;const height=[...this.host.children].reduce((sum,child)=>{const css=getComputedStyle(child);if(css.display==="none"||css.position==="absolute"||css.position==="fixed")return sum;count++;let block=child===this.grid?0:child.offsetHeight;if(child===this.notice&&child.classList.contains("cx-state")){block=(child.lastElementChild?child.lastElementChild.getBoundingClientRect().bottom-child.firstElementChild.getBoundingClientRect().top:0)+
|
|
903
|
+
parseFloat(css.paddingTop)+ parseFloat(css.paddingBottom)+ child.offsetHeight-child.clientHeight;block=Math.max(block,parseFloat(css.minHeight)||0)}
|
|
904
|
+
return sum+block+parseFloat(css.marginTop)+ parseFloat(css.marginBottom)},rows?Math.max(this.head.offsetHeight,this.rowH*Math.min(4,Math.max(1,this.rowCount())))+ 2:0);return height+this.host.offsetHeight-this.host.clientHeight+parseFloat(host.paddingTop)+ parseFloat(host.paddingBottom)
|
|
905
|
+
+ Math.max(0,count-1)*(parseFloat(host.rowGap)||0)},setLogicalTop(top){if(!this.hasGridSize()){this.hiddenScrollTop=top;return}
|
|
861
906
|
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
907
|
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
908
|
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 +912,7 @@ setClass(element,"cx-row" +(row.status?" cx-status cx-"+row.status:this.autoRows
|
|
|
867
912
|
}):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
913
|
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
914
|
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])}
|
|
915
|
+
if(this.phone||this.desktopHiddenFields||slot.hiddenFields||slot.phonePrimary)this.paintHiddenFields?.(slot,row.h,column,this.columnIndex[i])}
|
|
871
916
|
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
917
|
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
918
|
return this}
|
|
@@ -888,9 +933,13 @@ this.total=this.rowOffsets.top(count);this.syncScrollRange(this.logicalTop());th
|
|
|
888
933
|
if(changed){this.total=this.rowOffsets.top(this.rowOffsets.count);this.syncScrollRange(rows.rowTop(anchor.index)+ anchor.offset)}
|
|
889
934
|
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
935
|
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
|
|
892
|
-
|
|
893
|
-
const
|
|
936
|
+
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;const content=[...grid.children].reduce((sum,child)=>{const position=getComputedStyle(child).position;return position==="absolute"||position==="fixed"?sum:sum+child.getBoundingClientRect().height},grid.offsetHeight-grid.clientHeight);if(this.fitInline===undefined){this.fitInline=style.maxHeight;global.addEventListener("resize",()=>this.fitHeight(),{signal:this.lifetime.signal})}
|
|
937
|
+
if(/var\(/i.test(this.fitInline) && !this.fitBoundObserver) {
|
|
938
|
+
const observer=this.fitBoundObserver=new MutationObserver(records=>{if(records.some(record=>record.target!==this.host||record.attributeName==="class")||this.fitStyle!==style.cssText)this.fitHeight()});this.lifetime.signal.addEventListener("abort",()=>observer.disconnect(),{once:true})}
|
|
939
|
+
if(this.fitBoundObserver&&this.fitBoundParent!==this.host.parentElement){this.fitBoundObserver.disconnect();for(let parent=this.host;parent;parent=parent.parentElement)
|
|
940
|
+
this.fitBoundObserver.observe(parent,{attributes:true,attributeFilter:["style","class"]});this.fitBoundParent=this.host.parentElement}
|
|
941
|
+
const fitted=style.maxHeight;style.maxHeight=this.fitInline;const resolved=getComputedStyle(this.host).maxHeight;this.fitMax=resolved==="none"||/^(initial|inherit|unset|revert(-layer)?)$/.test(this.fitInline) ? resolved : this.fitInline || resolved;
|
|
942
|
+
style.maxHeight=fitted;const empty=!this.toolbar.hidden&&this.host.classList.contains("cx-grid-awaiting");const height=Math.ceil(Math.max(empty?0:this.host.getBoundingClientRect().height-grid.getBoundingClientRect().height,this.minimumGridHeight(false))+ content),bound=this.fitMax;const value=this.rowCount()||empty?bound==="none"?height+"px":"min("+height+"px, "+bound+")":this.fitInline;if(empty||this.fitEmpty)style.setProperty("--cx-min-h",empty?value:this.minimumGridHeight()+ "px");this.fitEmpty=empty;if(style.maxHeight!==value)style.maxHeight=value;this.fitStyle=style.cssText};function gridPriority(value){return["essential","normal","optional"].includes(value)||Number.isInteger(value)&&value>=0&&value<=MAX_PROTOCOL_INTEGER}
|
|
894
943
|
function gridRank(column){const value=column.priority;return typeof value==="number"?value:value==="optional"?10000:value==="essential"||value===undefined&&column.identity?0:5000}
|
|
895
944
|
function gridLevel(column){const rank=gridRank(column);return rank<2?0:rank<10000?1:2}
|
|
896
945
|
function gridOptions(column){return column.presenter||{}}
|
|
@@ -927,19 +976,21 @@ if(total>width&&remaining>1){column.fitHidden=true;total -=size(column);remainin
|
|
|
927
976
|
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
977
|
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
978
|
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,
|
|
979
|
+
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)
|
|
980
|
+
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)))}
|
|
981
|
+
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
982
|
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
983
|
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
984
|
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
985
|
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))}
|
|
986
|
+
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}
|
|
987
|
+
this.desktopDisclosureColumn=null;for(const column of this.columns){if(!column.fixed&&!column.sized)column.width=this.fitSize(column)}
|
|
988
|
+
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
989
|
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
990
|
(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
|
|
991
|
+
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("→")+
|
|
992
|
+
text(changeCellText(change.after,spec,this.fmt))+ 12));sampled++;continue}
|
|
993
|
+
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
994
|
(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
995
|
Math.min(kind==="longText"||kind==="tags"||kind==="list"||column.wrap?320:420,text(value))+
|
|
945
996
|
(kind==="progress"?80:kind==="tags"||kind==="list"?48:0));sampled++}
|
|
@@ -949,12 +1000,12 @@ return result};function validateGridHighlights(view){if(view.highlightRules===un
|
|
|
949
1000
|
rule.target!==undefined&&!["row","cell"].includes(rule.target)||rule.tone===undefined&&rule.className===undefined)
|
|
950
1001
|
throw new Error("CanopyX: invalid highlight rule.");const{column,operator,values}=rule;if(!Object.hasOwn(GRID_FILTER_LABELS,operator))throw new Error("CanopyX: highlight rules use built-in operators.");gridPredicate({column,operator,values},columns,budget);gridDecoration(rule.tone,rule.className)}}
|
|
951
1002
|
DataGrid.prototype.paintGridHighlights=function(element,row){if(!this.view.highlightRules?.length&&!row.h.tone&&!row.h.className&&!element._cxHighlight)return;const matches=new Map();if(!row.status)for(const rule of this.view.highlightRules||[]){const key=rule.target==="cell"?rule.column:null;if(!matches.has(key)&&gridMatches(row.h,rule,this.columns.find(column=>column.id===rule.column)))matches.set(key,rule)}
|
|
952
|
-
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;
|
|
1003
|
+
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-primary,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
1004
|
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
1005
|
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||
|
|
1006
|
+
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"||
|
|
1007
|
+
["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()}
|
|
1008
|
+
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
1009
|
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
1010
|
(![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
1011
|
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,16 +1020,17 @@ const value=raw.trim();try{const url=new URL(value,document.baseURI);if(["http:"
|
|
|
969
1020
|
}catch(_){/* Invalid destinations retain plain text. */ }
|
|
970
1021
|
return null}
|
|
971
1022
|
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)}}
|
|
1023
|
+
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
1024
|
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
1025
|
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
1026
|
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}
|
|
1027
|
+
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
1028
|
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
1029
|
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
1030
|
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()}
|
|
980
1031
|
if(focus){const element=this.rendered.get(row);if(this.hasGridSize()&&element)element.focus({preventScroll:true});else{this.focusOrigin=document.activeElement;this.scheduleRender()}}},onClick(event){const element=event.target.closest(".cx-row"),row=element&&element._w,link=event.target.closest("a.p-link");if(row&&link&&!this.emit("link",{id:row.key,row:gridFreeze(JSON.parse(JSON.stringify(row.h))),href:link.href,column:this.visibleColumns()[this.rowCells(element).findIndex(cell=>cell.contains(link))]?.id}))event.preventDefault();if(!row||event.defaultPrevented||event.target.closest("a,button,input,select,textarea,[contenteditable]"))return;this.setFocus(row,true);if(row.status){if(row.status==="error")this.retry().catch(()=>{});return}
|
|
981
|
-
|
|
1032
|
+
if(this.selectOnClick&&this.view.selection!=="none"){if(event.detail<2)this.selectClickedRow(row,event);return}
|
|
1033
|
+
const action=event.detail<2&&this.clickAction(element);if(action)this.activateRow(row,false,action!==true&&action,event)},selectClickedRow(row,event){if(this.pendingQuery)return;const additive=this.multi&&(event.ctrlKey||event.metaKey);if(this.multi&&event.shiftKey&&this.selectionAnchor!=null){this.selectRowRange(this.selectionAnchor,row._ri,{additive}).catch(error=>{if(error.name!=="AbortError")this.emit("error",{error})})}else{this.selectionController?.abort();const next=additive?new Set(this.selected):new Set();if(next.has(row.key))next.delete(row.key);else next.add(row.key);this.setSelection([...next]);this.selectionAnchor=row._ri}},clickAction(element){return this.view.rowAction==="link"&&element.querySelector("a[href]")||this.phone||this.view.rowAction==="details"},activateRow(row,keyboard,link,event={}){if(this.emit("rowActivate",{id:row.key,row:gridFreeze(JSON.parse(JSON.stringify(row.h))),keyboard}))link?
|
|
982
1034
|
link.dispatchEvent(new MouseEvent("click",{bubbles:true,cancelable:true,ctrlKey:event.ctrlKey,metaKey:event.metaKey,shiftKey:event.shiftKey})):
|
|
983
1035
|
Promise.resolve().then(()=>this.openDetails(row.key)).catch(error=>this.emit("error",{error}))},onKey(event){const element=event.target.closest(".cx-row"),row=element&&element._w;if(!row||event.defaultPrevented||event.isComposing||event.altKey)return;const cells=this.rowCells(element),navigation=this.pendingNavigation;const pendingPage=navigation&&navigation.focus&&navigation.epoch===this.queryEpoch&&this.pendingQuery&&
|
|
984
1036
|
this.pendingPageNavigation&&this.pendingPageNavigation.epoch===this.queryEpoch&&this.focused===row;let column=cells.findIndex(cell=>cell.contains(event.target));if(pendingPage||row.status&&navigation&&navigation.epoch===this.queryEpoch&&navigation.index===row._ri&&navigation.focus)
|
|
@@ -1003,99 +1055,128 @@ if(event.key!=="F2")return false;GRID_DRAG.picked=gridDragItem(this,row);setText
|
|
|
1003
1055
|
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
1056
|
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
1057
|
}}
|
|
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)||
|
|
1058
|
+
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
1059
|
!Number.isFinite(state.scrollTop)||state.scrollTop<0||state.scrollTop>MAX_PROTOCOL_INTEGER*this.rowH||!Array.isArray(state.selection))
|
|
1008
1060
|
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
1061
|
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
1062
|
!Number.isInteger(paging.offset)||paging.offset<0||paging.offset>MAX_PROTOCOL_INTEGER||this.cursor&&paging.offset!==0)
|
|
1011
1063
|
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||
|
|
1064
|
+
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
1065
|
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
1066
|
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
|
|
1067
|
+
}});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)||
|
|
1068
|
+
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))}
|
|
1069
|
+
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
1070
|
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
1071
|
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
1072
|
const parsed=typeof value==="string"&&/^\d{4}-\d\d-\d\dT\d\d:\d\d/.test(value) ? new Date(value) : null;
|
|
1019
1073
|
if(parsed&&Number.isFinite(parsed.getTime()))return calInstantText(parsed,field,allDay);throw new Error("CanopyX: a calendar event's "+field+" is a Date or an ISO date or date and time.")}
|
|
1020
1074
|
function createCalendar(host,options={}){const preset=CAL_PRESETS[options.preset??"basic"];if(!preset)throw new Error("CanopyX: calendar presets are basic, standard and scheduler.");const events=options.events??[];if(!Array.isArray(events)||events.length>100000)throw new Error("CanopyX: calendar events are an array of at most 100,000 events.");const hooks={},own={};for(const[name,value]of Object.entries(options.calendar||{}))(typeof value==="function"?hooks:own)[name]=value;const pick=name=>own[name]!==undefined?own[name]:options[name]!==undefined?options[name]:preset[name];const editable=!!pick("editable"),create=pick("create"),drop=!!pick("drop"),used=new Set(),ids=new Set();const rows=events.map((event,index)=>{if(!event||typeof event!=="object"||typeof event.title!=="string")throw new Error("CanopyX: calendar event " +(index+1)+ " needs a title.");const id=event.id===undefined?"e" +(index+1):String(event.id);if(ids.has(id))throw new Error("CanopyX: calendar event ids are unique; '"+id+"' repeats.");ids.add(id);const allDay=event.allDay??(typeof event.start==="string"&&event.start.length===10);const cells={title:event.title,start:calInstantText(event.start,"start",allDay)};if(event.end!=null)cells.end=calInstantText(event.end,"end",allDay);if(allDay){cells.allDay=true;used.add("allDay")}
|
|
1021
1075
|
for(const field of CAL_FIELDS)if(field!=="allDay"&&event[field]!=null){cells[field]=field==="resourceIds"?JSON.stringify(event[field]):field==="recurrenceId"?calInstantText(event[field],field,false):String(event[field]);used.add(field)}
|
|
1022
|
-
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})}
|
|
1076
|
+
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"]};const opensOnTimeline=pick("view")==="timeline";if(opensOnTimeline&&options.timeline===false)throw new Error("CanopyX: a calendar that opens on the timeline cannot set timeline: false.");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||opensOnTimeline||options.preset==="scheduler"))addTimeline();if(options.preset==="scheduler")calendar.capacity={thresholds:[{at:3,tone:"warning"},{at:5,tone:"danger"}]}}else if(options.timeline||opensOnTimeline)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
1077
|
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
1078
|
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:"
|
|
1079
|
+
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
1080
|
throw new Error("CanopyX: invalid saved facet panel state.");if(this.view.facets||this.opts.facets)state.facets={open:facets.open}}
|
|
1027
1081
|
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
|
-
|
|
1082
|
+
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,validateGridPage,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)};DataGrid.prototype.selectRowRange=async function(start,end,options={}){this.selectionController?.abort();const controller=this.selectionController=new AbortController(),epoch=this.queryEpoch;try{await loadPart(GRID_OPTIONS_PART);if(controller.signal.aborted||epoch!==this.queryEpoch)throw new DOMException("Selection was superseded.","AbortError");return await this.readSelectionRange(start,end,options,controller)}finally{controller.abort();if(this.selectionController===controller)this.selectionController=null}};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())
|
|
1083
|
+
.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)}
|
|
1084
|
+
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")||
|
|
1085
|
+
!!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
1086
|
(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
|
-
|
|
1087
|
+
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{}
|
|
1088
|
+
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)}
|
|
1089
|
+
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)}
|
|
1090
|
+
catch(error){showError(error);throw error}}
|
|
1091
|
+
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}
|
|
1092
|
+
const MATRIX_API={createMatrix};class TimeSliderHost{}
|
|
1093
|
+
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()}}
|
|
1094
|
+
class ChangesHost{}
|
|
1036
1095
|
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
1096
|
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
1097
|
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};
|
|
1098
|
+
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}}
|
|
1099
|
+
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"],["column-groups","getColumnBands"],["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)})}}}
|
|
1100
|
+
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.columnBands??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)})}}
|
|
1101
|
+
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}
|
|
1102
|
+
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
1103
|
let keyboardInput=false;if(typeof document!=="undefined"){document.addEventListener("keydown",()=>{keyboardInput=true},true);document.addEventListener("pointerdown",()=>{keyboardInput=false},true)}
|
|
1043
1104
|
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
1105
|
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
|
|
1106
|
+
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("role","dialog");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
1107
|
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
1108
|
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(
|
|
1109
|
+
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}
|
|
1110
|
+
this.setQuery(gridQueryStart(this.queryForChange(),{search:input.value})).then(()=>input.setCustomValidity(""))
|
|
1111
|
+
.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?
|
|
1112
|
+
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})})}
|
|
1113
|
+
(query.tupleFilters||[]).forEach((tuple,index)=>{const condition=tuple.columns.map(field=>this.columns.find(column=>column.id===field.column).title).join(", ")+
|
|
1114
|
+
(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
1115
|
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
1116
|
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
1117
|
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
1118
|
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
1119
|
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
1120
|
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)}}
|
|
1121
|
+
{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")}
|
|
1122
|
+
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}
|
|
1123
|
+
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
1124
|
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
1125
|
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
|
-
|
|
1126
|
+
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)))}
|
|
1127
|
+
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}
|
|
1128
|
+
return result}
|
|
1129
|
+
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}
|
|
1130
|
+
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
1131
|
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
1132
|
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
1133
|
for(const column of[...grid.cols].reverse())if(column.effectivePin==="right"){column.pinOffset=end;end+=width(column)}
|
|
1068
1134
|
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)}
|
|
1135
|
+
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
1136
|
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
|
-
|
|
1137
|
+
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)}}
|
|
1138
|
+
catch(error){if(!this.destroyed&&this.pendingColumnMenu===request){this.setNotice(this.labels.gridLoadFailed,true);this.emit("error",{error})}}
|
|
1139
|
+
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}
|
|
1140
|
+
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}]}
|
|
1141
|
+
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))
|
|
1142
|
+
.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")}
|
|
1143
|
+
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
1144
|
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))
|
|
1145
|
+
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)})}
|
|
1146
|
+
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
1147
|
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:
|
|
1148
|
+
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
1149
|
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
1150
|
index<(this.cursor?this.pageOffset:0)||index>=this.rowCount())
|
|
1081
1151
|
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
1152
|
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
1153
|
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
|
-
|
|
1154
|
+
.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)}
|
|
1155
|
+
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)
|
|
1156
|
+
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):
|
|
1157
|
+
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}
|
|
1158
|
+
tokenColumns.add(slot);const inputs=[...text.matchAll(/\{([^{}]+)\}/g)].map(match => match[1]);
|
|
1159
|
+
if(inputs.some(id=>!this.columns.some(column=>column.id===id&&column.exportable!==false))){template[slot]=null;continue}
|
|
1160
|
+
for(const id of inputs){tokenColumns.add(id);if(!columns.includes(id))columns.push(id)}}
|
|
1161
|
+
capturedOptions={...capture,columns}}}
|
|
1162
|
+
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?
|
|
1163
|
+
this.exportFromMenu((options,onProgress)=>this.print({...options,onProgress})):this.print({scope:"filtered"}))
|
|
1164
|
+
.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())}}
|
|
1165
|
+
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.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 hidden=first&&(this.phone||this.desktopHiddenFields)?this.hiddenViewColumns().filter(column=>!fields.includes(column)):[],count=hidden.length;if(!count){button?.remove();slot.hiddenFields=null}
|
|
1087
1166
|
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.
|
|
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}
|
|
1167
|
+
const label=fill(plural(this.labels.hiddenColumns,count,this.lang),{count:numberFormat(this.lang).format(count)}),titles=hidden.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}
|
|
1168
|
+
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}
|
|
1169
|
+
if(first&&count)slot.cell.appendChild(button);return}
|
|
1091
1170
|
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
1171
|
let line=slot.phoneLine;if(!line||line.parentNode!==slot.cell){line=h("span","cx-phone-fields");slot.cell.appendChild(line)}
|
|
1093
1172
|
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
|
-
if(count&&button.parentNode!==
|
|
1095
|
-
{
|
|
1173
|
+
const disclosure=fields.length?line.lastElementChild:line;if(count&&button.parentNode!==disclosure)disclosure.appendChild(button);fields.forEach((field,index)=>{this.paintSlot(slot.childSlots[index],row,field,this.columns.indexOf(field),{level:0,expanded:false});const value=slot.childSlots[index].cell;value.title=cellText(row.cells[field.id],field,this.fmt)||""})};DataGrid.prototype.syncGridPhoneValues=function(){if(!this.phone)return;for(const value of this.canvas.querySelectorAll(".cx-phone-value")){if(value.querySelector(".p-pill,.p-state")&&value.scrollWidth>value.clientWidth+1)value.tabIndex=0;else value.removeAttribute("tabindex")}};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.");if(options.recordDrawerModal!==undefined&&typeof options.recordDrawerModal!=="boolean")
|
|
1174
|
+
throw new Error("CanopyX: recordDrawerModal must be a Boolean.");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&&
|
|
1175
|
+
{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
1176
|
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
|
-
|
|
1177
|
+
GRID_REPORTING.api?.(api,grid);for(const name of["getViewIntent","validateViewIntent","previewViewIntent","applyViewIntent"])
|
|
1178
|
+
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.defineProperty(api,"selectOnClick",{enumerable:true,get:()=>grid.selectOnClick,set:value=>{if(typeof value!=="boolean")throw new Error("CanopyX: selectOnClick is a boolean.");grid.selectOnClick=value}});Object.freeze(api);activeGrids.set(host,api);host.setAttribute("data-canopyx-mounted","true");host.canopyx=api;recordStart(grid);return api}
|
|
1179
|
+
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
1180
|
function checkBranch(){return{value:null,mask:0,children:new Map()}}
|
|
1100
1181
|
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
1182
|
if(branch.value!==null)throw new Error("CanopyX: a checking path cannot have duplicate intents.");branch.value=item.checked}
|
|
@@ -1175,21 +1256,14 @@ cancelInteraction(){const operation=this.pendingInteraction;if(!operation)return
|
|
|
1175
1256
|
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
1257
|
withInteractionReplacement(work){this.interactionReplacementDepth=(this.interactionReplacementDepth||0)+ 1;try{return work()}
|
|
1177
1258
|
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()
|
|
1259
|
+
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()}
|
|
1260
|
+
buildToolbar(){super.buildToolbar();if(!(this.opts.onEdit&&this.columns.some(column=>column.editable)||
|
|
1261
|
+
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
1262
|
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}
|
|
1263
|
+
openInteractionPanel(w,anchor,role,label){return UI_IMPL.treeActions.openInteractionPanel.call(this,w,anchor,role,label)}
|
|
1264
|
+
closeInteractionPanel(restoreFocus=true){return UI_IMPL.treeActions?.closeInteractionPanel.call(this,restoreFocus)||null}
|
|
1184
1265
|
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}}}}
|
|
1266
|
+
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
1267
|
super.onKey(event)}
|
|
1194
1268
|
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
1269
|
refreshCounts(ids){return this.withInteractionReplacement(()=>super.refreshCounts(ids))}
|
|
@@ -1204,19 +1278,9 @@ const result=super.reconcileList(parent,...args);focus=focus||this.interactionFo
|
|
|
1204
1278
|
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
1279
|
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
1280
|
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}
|
|
1281
|
+
onKey(event){if(UI_IMPL.treeActions?.onKey.call(this,event,hostControl))return;super.onKey(event)}
|
|
1282
|
+
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())}}
|
|
1283
|
+
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
1284
|
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
1285
|
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
1286
|
this.dragLease=null},{signal})}
|
|
@@ -1253,9 +1317,9 @@ fields.operator.appendChild(gridOption("between",this.labels.gridBetween));upper
|
|
|
1253
1317
|
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
1318
|
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
1319
|
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
|
|
1320
|
+
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:
|
|
1321
|
+
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})}
|
|
1322
|
+
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
1323
|
function reportingWidth(grid,column,width){const policy=reportingLayout(grid,column);return Math.min(policy.maximum,Math.max(policy.minimum,width))}
|
|
1260
1324
|
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
1325
|
!Number.isInteger(policy.minimum)||policy.minimum<40||!Number.isInteger(policy.maximum)||
|
|
@@ -1281,7 +1345,7 @@ throw new Error("CanopyX: invalid responsive flag or minimum center width.");for
|
|
|
1281
1345
|
policy.alwaysVisible!==undefined&&typeof policy.alwaysVisible!=="boolean")
|
|
1282
1346
|
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
1347
|
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)=>
|
|
1348
|
+
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
1349
|
(reportingLayout(this,b).priority??MAX_PROTOCOL_INTEGER)-(reportingLayout(this,a).priority??MAX_PROTOCOL_INTEGER)||
|
|
1286
1350
|
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
1351
|
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 +1356,7 @@ const PLACEMENT_HEADER=DataGrid.prototype.buildHeader,PLACEMENT_MOVE=DataGrid.pr
|
|
|
1292
1356
|
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
1357
|
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
1358
|
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))||
|
|
1359
|
+
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
1360
|
(value.version!==undefined||value.view!==undefined)&&(value.version!==1||value.view!==grid.view.id)||
|
|
1297
1361
|
!Array.isArray(value.views)||value.views.length>20)
|
|
1298
1362
|
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 +1375,7 @@ catch(error){this.viewSettingsIntent=intent;throw error}},removeSavedView(name){
|
|
|
1311
1375
|
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
1376
|
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
1377
|
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)
|
|
1378
|
+
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
1379
|
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
1380
|
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
1381
|
while(pending){try{await pending;if(pending===this.viewQueryPromise)return this.getQuery()}
|
|
@@ -1336,35 +1400,28 @@ this.selectionScopeKey=key;this.selectionRevision=detail.revision}
|
|
|
1336
1400
|
if(name==="select")detail={...detail,selection:this.getSelectionDescriptor()};if(name==="select"||name==="query"||name==="error")this.syncSelectionControls?.()}
|
|
1337
1401
|
return SELECTION_BASE.emit.call(this,name,detail)},copyFocused(row,cell){return this.querySelection?this.copy({scope:"selected"}):SELECTION_BASE.copyFocused.call(this,row,cell)}});Object.assign(DataGrid.prototype,{selectPage(selected=true,options={}){this.requireReportingSelection(true);if(typeof selected!=="boolean"||options.signal!==undefined&&!(options.signal instanceof AbortSignal))
|
|
1338
1402
|
throw new Error("CanopyX: page selection needs a boolean and optional AbortSignal.");if(!this.paged)throw new Error("CanopyX: page selection requires paged presentation.");const bounds=this.rowBounds();return bounds.last<bounds.first?Promise.resolve(this.getSelectionDescriptor()):
|
|
1339
|
-
this.selectRange(bounds.first,bounds.last,{...options,selected,additive:true})},async selectRange(start,end,options={}){this.requireReportingSelection(true);
|
|
1340
|
-
typeof selected!=="boolean"||typeof additive!=="boolean"||options.signal!==undefined&&!(options.signal instanceof AbortSignal))
|
|
1341
|
-
throw new Error("CanopyX: selection ranges need accepted positions and at most 10,000 records.");if(this.cursor&&(first<this.pageOffset||last>=this.cursorWindow.end))
|
|
1342
|
-
throw new Error("CanopyX: sequential-source selection ranges stay on the accepted page.");this.selectionController?.abort();const controller=this.selectionController=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 query=this.acceptedQuery,snapshot={...this.snapshot},ids=[],seen=new Set(),scope=this.querySelection;try{gridOperationActive(this,controller.signal);if(this.cursor){for(let index=first;index<=last;index++){const row=this.rowCache.get(index);if(!row||row.status)throw new Error("CanopyX: the complete accepted cursor page is required for page/range selection.");ids.push(row.key)}}else for(let offset=Math.floor(first / this.pageSize) * this.pageSize; offset <= last; offset += this.pageSize) {
|
|
1343
|
-
const request=gridQuery(this.view,{...query,offset,limit:this.pageSize});const page=validateGridPage(await this.requestPage(request,controller.signal),this.view,request,snapshot);gridOperationActive(this,controller.signal);for(const[index,row]of page.items.entries()){if(seen.has(row.id))throw new Error("CanopyX: a selection range repeated a record identity.");seen.add(row.id);if(page.offset+index>=first&&page.offset+index<=last)ids.push(row.id)}}
|
|
1344
|
-
gridOperationActive(this,controller.signal);if(epoch!==this.queryEpoch||ids.length!==last-first+1)
|
|
1345
|
-
throw new DOMException("Selection range was superseded or incomplete.","AbortError");if(scope){const excluded=new Set(this.selectionExcluded);for(const id of ids)if(selected)excluded.delete(id);else excluded.add(id);if(excluded.size>REPORTING_SELECTION_LIMIT)throw new Error("CanopyX: query selection supports at most 10,000 exclusions.");this.selectionExcluded=excluded;this.publishQuerySelection()}else{const next=additive||!selected?new Set(this.selected):new Set();for(const id of ids)if(selected)next.add(id);else next.delete(id);this.setSelection([...next])}
|
|
1346
|
-
if(this.selectionAnchor==null)this.selectionAnchor=start;return this.getSelectionDescriptor()}finally{dataSignal.removeEventListener("abort",abort);options.signal?.removeEventListener("abort",abort);controller.abort();if(this.selectionController===controller)this.selectionController=null}}});const SELECTION_EXPORT_PREPARE=DataGrid.prototype.prepareExport,SELECTION_EXPORT_ROWS=DataGrid.prototype.exportRows,SELECTION_EXPORT_TEXT=DataGrid.prototype.exportText;DataGrid.prototype.prepareExport=function(options){if(options?.scope!=="selected"||!this.querySelection)return SELECTION_EXPORT_PREPARE.call(this,options);const selection=this.getSelectionDescriptor(),exported=SELECTION_EXPORT_PREPARE.call(this,{...options,scope:"filtered"});const request=gridFreeze({...exported.request,scope:"selected",query:selection.query,revision:selection.revision,matched:selection.matched,recordCount:selection.matched===null?null:selection.matched-selection.excludedIds.length,selection:[],querySelection:selection});const signal=options.signal;return Object.freeze({request,rows:options=>this.exportRows(request,gridExportSignal(signal,options))})};DataGrid.prototype.exportRows=async function*(request,signal){if(!request.querySelection){yield*SELECTION_EXPORT_ROWS.call(this,request,signal);return}
|
|
1403
|
+
this.selectRange(bounds.first,bounds.last,{...options,selected,additive:true})},async selectRange(start,end,options={}){this.requireReportingSelection(true);await this.selectRowRange(start,end,options);return this.getSelectionDescriptor()}});const SELECTION_EXPORT_PREPARE=DataGrid.prototype.prepareExport,SELECTION_EXPORT_ROWS=DataGrid.prototype.exportRows,SELECTION_EXPORT_TEXT=DataGrid.prototype.exportText;DataGrid.prototype.prepareExport=function(options){if(options?.scope!=="selected"||!this.querySelection)return SELECTION_EXPORT_PREPARE.call(this,options);const selection=this.getSelectionDescriptor(),exported=SELECTION_EXPORT_PREPARE.call(this,{...options,scope:"filtered"});const request=gridFreeze({...exported.request,scope:"selected",query:selection.query,revision:selection.revision,matched:selection.matched,recordCount:selection.matched===null?null:selection.matched-selection.excludedIds.length,selection:[],querySelection:selection});const signal=options.signal;return Object.freeze({request,rows:options=>this.exportRows(request,gridExportSignal(signal,options))})};DataGrid.prototype.exportRows=async function*(request,signal){if(!request.querySelection){yield*SELECTION_EXPORT_ROWS.call(this,request,signal);return}
|
|
1347
1404
|
const excluded=new Set(request.querySelection.excludedIds);const filtered={...request,scope:"filtered"};gridCopyExportPresentation(request,filtered);for await(const row of SELECTION_EXPORT_ROWS.call(this,filtered,signal))
|
|
1348
1405
|
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
1406
|
selection.matched-selection.excludedIds.length>maxRows)
|
|
1350
1407
|
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}}
|
|
1408
|
+
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
1409
|
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");
|
|
1410
|
+
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
1411
|
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
1412
|
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
1413
|
!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}
|
|
1357
1414
|
try{SELECTION_CONTROLS_BASE.onClick.call(this,event)}
|
|
1358
1415
|
catch(error){if(!this.view.reporting?.selectionControls)throw error;this.runReportingSelection(()=>{throw error});return}
|
|
1359
|
-
if(this.view.reporting?.selectionControls&&row&&!row.status&&!event.defaultPrevented&&
|
|
1416
|
+
if(!this.selectOnClick&&this.view.reporting?.selectionControls&&row&&!row.status&&!event.defaultPrevented&&
|
|
1360
1417
|
!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
1418
|
!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:"
|
|
1419
|
+
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
1420
|
const reportingRowsUsed=grid=>reportingSummaryEnabled(grid.view)||!!grid.view.reporting?.detail||!!grid.opts.detail;function reportingGroupKey(value){return JSON.stringify([typeof value,value])}
|
|
1364
1421
|
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))||
|
|
1422
|
+
Object.keys(level).some(key=>!["column","direction","aggregate","bucket"].includes(key))||
|
|
1366
1423
|
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})})})}
|
|
1424
|
+
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
1425
|
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
1426
|
!["asc","desc"].includes(grouping.direction)||
|
|
1370
1427
|
!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,47 +1440,54 @@ detail.label!==undefined&&(typeof detail.label!=="string"||!detail.label.trim()|
|
|
|
1383
1440
|
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
1441
|
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
1442
|
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:
|
|
1443
|
+
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
1444
|
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
1445
|
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
1446
|
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
1447
|
query.groupBy.filter(level=>level&&this.columns.some(column=>column.id===level.column&&column.sortable)).slice(0,GROUP_LEVELS)
|
|
1391
1448
|
.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
1449
|
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}
|
|
1450
|
+
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)}
|
|
1451
|
+
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, "%");
|
|
1452
|
+
const format=aggregate.format||(!reportingCounting(aggregate.operation)&&column?.format);return mode==="rank"?numberText(value,grid.lang,{minimumFractionDigits:0,maximumFractionDigits:0}):
|
|
1453
|
+
format?numberText(value,grid.lang,format):numberText(value,grid.lang,{minimumFractionDigits:reportingCounting(aggregate.operation)?0:2,maximumFractionDigits:reportingCounting(aggregate.operation)?0:2})}
|
|
1454
|
+
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);const labelled=fill(grid.labels[AGGREGATE_LABELS[aggregate.operation]]||grid.groupAggregates?.get(aggregate.operation)?.label||aggregate.operation+" {value}",{value:shown});text.push(mode==="value"?labelled.charAt(0).toLocaleUpperCase(grid.lang)+ labelled.slice(1):shown+" "+caption.replace(/^% /, ""));
|
|
1455
|
+
titles.push(reportingAggregateName(grid,aggregate)+ ": "+shown +(caption?" ("+caption+")":""))}
|
|
1456
|
+
return{text:text.join(" · "),title:titles.join(" · "),caption:""}}
|
|
1457
|
+
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)}
|
|
1458
|
+
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)}
|
|
1459
|
+
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
1460
|
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
1461
|
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
1462
|
const safe=gridPresenterLink(target);if(safe&&/^(https?:|mailto:)/i.test(safe)) link = { target: safe, ...(object && cell.title ? { tooltip: cell.title } : {}) };
|
|
1408
1463
|
else diagnostics.push("unsafe-link")}else if(target)diagnostics.push("relative-link")}
|
|
1409
1464
|
const mark=matches.get(column.id),tone=mark?.tone??columnValueTone(column,cell)??(presenter?.kind==="boolean"&&value===true&&presenter.inverted?"danger":
|
|
1410
1465
|
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}
|
|
1466
|
+
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
1467
|
function reportingFileName(value){if(typeof value!=="string"||!value||value.length>100||value!==value.trim()||
|
|
1413
1468
|
/[\\/:*?"<>|\x00-\x1f\x7f-\x9f\u2028\u2029\u202a-\u202e\u2066-\u2069]/u.test(value) ||
|
|
1414
1469
|
/^\.|[. ]$/.test(value) || /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(value))
|
|
1415
1470
|
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
1471
|
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
1472
|
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)
|
|
1473
|
+
async function reportingExportSnapshot(grid,exported,maxRows,signal,progress){await loadExportPart();return EXPORT_IMPL.snapshot(grid,exported,maxRows,signal,progress)}
|
|
1474
|
+
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"))
|
|
1475
|
+
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
1476
|
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
|
-
|
|
1477
|
+
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));
|
|
1478
|
+
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)}
|
|
1479
|
+
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)||
|
|
1480
|
+
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||
|
|
1481
|
+
values.some(id=>!view.reporting.aggregates?.some(aggregate=>aggregate.id===id))||
|
|
1482
|
+
!Number.isInteger(maxColumns)||maxColumns<1||maxColumns>200||
|
|
1483
|
+
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))||
|
|
1484
|
+
!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}})})}
|
|
1485
|
+
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}
|
|
1486
|
+
function pivotIntent(grid){if(grid.pivotState===undefined)grid.pivotState=pivotDefaultState(grid.view);return grid.pivotState}
|
|
1487
|
+
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)}}
|
|
1488
|
+
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),{}))}
|
|
1489
|
+
catch(error){this.pivotState=previous;this.render();throw error}
|
|
1490
|
+
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
1491
|
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
1492
|
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
1493
|
catch(error){body.drawn=false;body.replaceChildren();this.emit("error",{error})}}
|
|
@@ -1435,7 +1499,8 @@ const FLOW_ASYNC_METHODS=["next","back","goTo","setState","validate","submit","r
|
|
|
1435
1499
|
.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
1500
|
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
1501
|
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.")}
|
|
1438
|
-
|
|
1502
|
+
if(options.recordDrawerModal!==undefined&&typeof options.recordDrawerModal!=="boolean")
|
|
1503
|
+
throw new Error("CanopyX: recordDrawerModal must be a Boolean.");const existing=activeGrids.get(host);if(existing)return existing;const grid=new TreeGrid(host,options);const api=Object.assign({element:host,view:grid.view,ready:startPlugins(grid,()=>api),expandPath:(ids,o)=>grid.expandPath(ids,o),setFilter:(filter,o)=>{if(!grid.queryAware)grid.clearDeclaredFilters();grid.setFilter(filter,o)},setFilterActive:(id,active)=>grid.setFilterActive(id,active),setActiveFilters:(ids)=>grid.setActiveFilters(ids),updateActiveFilters:(change)=>grid.updateActiveFilters(change),getActiveFilters:()=>Array.from(grid.activeFilters),sort:(columnId,direction)=>grid.sort(columnId,direction),getQuery:()=>grid.getQuery(),setQuery:(query,options)=>grid.setQuery(query,options),setColumnVisible:(columnId,visible)=>grid.setColumnVisible(columnId,visible),setColumnWidth:(columnId,width)=>grid.setColumnWidth(columnId,width),setTreeColumnPinned:(pinned)=>grid.setTreeColumnPinned(pinned),setTimeZone:(zone)=>grid.setTimeZone(zone),getTimeZone:()=>(grid.local?"local":"utc"),refresh:()=>{grid.version++;grid.scheduleRender()},reload:(id,options)=>grid.reload(id,options),readLocation:()=>grid.readLocation(),getSelected:()=>Array.from(grid.selected).map((w)=>w.h),setSelection:(ids)=>grid.setSelection(ids),clearSelection:()=>grid.setSelection([]),selectAll:(options)=>grid.selectAll(options),focus:()=>{if(grid.destroyed)return;const target=grid.indexOf(grid.focused)>=0?grid.focused:grid.rows[0];if(target)grid.setFocus(target,true);else grid.grid.focus({preventScroll:true})},visibleRows:()=>grid.visibleRows(),toOutline:()=>grid.toOutline(),getState:()=>grid.getState(),setState:(state)=>grid.setState(state),getLayout:()=>grid.getLayout(),setLayout:(change)=>grid.setLayout(change),getMatches:()=>grid.getMatches(),setSearchHighlight:(enabled)=>grid.setSearchHighlight(enabled),nextMatch:(options)=>grid.nextMatch(options),previousMatch:(options)=>grid.previousMatch(options),search:(query,limit)=>grid.search(query,limit||(grid.view.search&&grid.view.search.maxResults)||20),loadMore:(id)=>{const w=id==null?grid.root:grid.keyMap.get(id);return w?grid.loadMore(w):Promise.resolve()},applyCounts:(doc)=>grid.applyCounts(doc),refreshCounts:(ids)=>grid.refreshCounts(ids),closeMenus:()=>grid.closeMenus?.(),destroy:()=>{grid.destroy();if(activeGrids.get(host)===api)activeGrids.delete(host);if(host.canopyx===api)delete host.canopyx}},grid.interactionApi&&grid.interactionApi(),linkApi(grid));for(const name of["prepareExport","exportAs","getSavedViews","saveView","applySavedView","removeSavedView","resetView","setColumnOrder","resetColumns","setColumnPinned","openDetails","openRecord"])
|
|
1439
1504
|
api[name]=(...args)=>{if(grid[name])return grid[name](...args);const error=new Error("CanopyX: "+name+" needs the tree tools part.");if(/^(exportAs|applySavedView|resetView|openDetails|openRecord)$/.test(name)) return Promise.reject(error);
|
|
1440
1505
|
throw error};api.ready=linkReady(grid,api.ready);Object.freeze(api);activeGrids.set(host,api);host.setAttribute("data-canopyx-mounted","true");host.canopyx=api;recordStart(grid);return api}
|
|
1441
1506
|
const configured=new Map();function configure(viewId,options){requireIdentifier(viewId,"view ID");if(!options||typeof options!=="object"){throw new Error("CanopyX: configure needs a view id and an options object.")}
|
|
@@ -1448,5 +1513,5 @@ watcher.observe(host)}
|
|
|
1448
1513
|
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
1514
|
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
1515
|
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);
|
|
1516
|
+
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}
|
|
1517
|
+
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);
|