@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
|
@@ -77,7 +77,8 @@ function fill(template,values){return template.replace(/\{(\w+)\}/g, (match, nam
|
|
|
77
77
|
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];
|
|
78
78
|
let rules=pluralRules.get(lang);if(!rules){try{rules=new Intl.PluralRules(lang||"en")}catch(_){rules=new Intl.PluralRules("en")}pluralRules.set(lang,rules)}
|
|
79
79
|
return forms[rules.select(Number(count))]??forms.other??template}
|
|
80
|
-
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)=>{
|
|
80
|
+
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);
|
|
81
|
+
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 + "'.");
|
|
81
82
|
if(typeof source[name]!=="string"||!/\S/.test(source[name])) throw new Error("CanopyX: label '" + name + "' must be a string with visible text.");
|
|
82
83
|
if((name==="utcTime"||name==="localTime")&&source[name].indexOf("{time}")<0)throw new Error("CanopyX: label '"+name+"' must hold {time}.");labels[name]=source[name]})}
|
|
83
84
|
return Object.freeze(labels)}
|
|
@@ -91,13 +92,13 @@ function trusted(kind,value){if(trustPolicy===undefined){try{trustPolicy=global.
|
|
|
91
92
|
catch(_){trustPolicy=null}}
|
|
92
93
|
return trustPolicy?trustPolicy["create"+kind](value):value}
|
|
93
94
|
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.")}
|
|
94
|
-
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;
|
|
95
|
-
if(
|
|
95
|
+
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}}}
|
|
96
|
+
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}
|
|
96
97
|
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)}
|
|
97
98
|
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)}
|
|
98
99
|
return copied}
|
|
99
100
|
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}
|
|
100
|
-
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}
|
|
101
|
+
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();
|
|
101
102
|
}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(); }
|
|
102
103
|
else if(c==="]"){closed=true;break}}
|
|
103
104
|
if(!closed)bad()}else if("()|^$*+?{}]".includes(unit))bad();let count=1;if(i<source.length&&"*+?".includes(source[i])){variable++;i++}
|
|
@@ -143,15 +144,18 @@ if(typeof value!=="string"&&typeof value!=="boolean")throw new Error("CanopyX: t
|
|
|
143
144
|
function gridExactTokens(column){return column.kind==="text"&&(column.valueOrder?.length||Object.keys(column.valueLabels||{}).length)}
|
|
144
145
|
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}
|
|
145
146
|
return false}
|
|
146
|
-
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]));
|
|
147
|
-
throw new Error("CanopyX: invalid flat row fields.");
|
|
147
|
+
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))
|
|
148
|
+
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}
|
|
149
|
+
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)}
|
|
150
|
+
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)
|
|
151
|
+
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}
|
|
152
|
+
function validateGridCell(cell,column){if(isCellObject(cell)){if(Array.isArray(cell)||
|
|
148
153
|
["text","searchText","title","tone","secondary"].some(name=>cell[name]!==undefined&&typeof cell[name]!=="string")||
|
|
149
154
|
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)||
|
|
150
155
|
cell.badges!==undefined&&(!Array.isArray(cell.badges)||cell.badges.some(badge=>!badge||typeof badge.text!=="string"||
|
|
151
156
|
["tone","title"].some(name=>badge[name]!==undefined&&typeof badge[name]!=="string"))))
|
|
152
157
|
throw new Error("CanopyX: malformed grid cell.");if(typeof cell.sort==="number")gridScalar(cell.sort,{kind:"number"})}
|
|
153
|
-
gridScalar(cell,
|
|
154
|
-
return rows}
|
|
158
|
+
gridScalar(cell,column)}
|
|
155
159
|
function gridDecoration(tone,className){if(tone!==undefined&&(typeof tone!=="string"||!/^[a-z][a-z0-9-]{0,31}$/.test(tone) || tone.endsWith("-bg")) ||
|
|
156
160
|
className!==undefined&&(typeof className!=="string"||!className.trim()||className.length>1024||/[\u0000-\u001f\u007f-\u009f]/.test(className)))
|
|
157
161
|
throw new Error("CanopyX: invalid highlight tone or className.")}
|
|
@@ -212,8 +216,9 @@ if(kind==="inline"){if(!Array.isArray(view.roots))throw new Error("CanopyX: inli
|
|
|
212
216
|
throw new Error("CanopyX: unknown source kind '"+kind+"'.")}
|
|
213
217
|
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)}
|
|
214
218
|
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))}}
|
|
215
|
-
class TreeGrid{constructor(host,options){const view=checkDocument(options.view,"view");
|
|
216
|
-
|
|
219
|
+
class TreeGrid{constructor(host,options){const view=checkDocument(options.view,"view");if(view.recordDrawerModal!==undefined&&typeof view.recordDrawerModal!=="boolean")
|
|
220
|
+
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.")}
|
|
221
|
+
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"))
|
|
217
222
|
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.")}
|
|
218
223
|
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||[]}
|
|
219
224
|
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()}
|
|
@@ -280,7 +285,7 @@ value.sort===undefined||typeof value!=="object")throw new Error("CanopyX: query
|
|
|
280
285
|
const named=new Set(value.filters);if(named.size!==value.filters.length||value.filters.some(id=>!(grid.view.filters||[]).some(f=>f.id===id)))
|
|
281
286
|
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})}
|
|
282
287
|
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}
|
|
283
|
-
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")}
|
|
288
|
+
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")}
|
|
284
289
|
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++}
|
|
285
290
|
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)}
|
|
286
291
|
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}}
|
|
@@ -408,7 +413,7 @@ if(accepted&&deferred&&revision===this.selectionRevision)
|
|
|
408
413
|
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}
|
|
409
414
|
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}}
|
|
410
415
|
return true},announce(message){setText(this.live,message)}});const CELL_CONTROLS="a[href],button,input,select,textarea,summary,[tabindex],[contenteditable]:not([contenteditable=false])," +
|
|
411
|
-
"[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]," +
|
|
416
|
+
"[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]," +
|
|
412
417
|
"[role=textbox],[role=combobox],[role=spinbutton],[role=slider],[role=radio],[role=tab],[role=menuitem],[role=listbox],[role=tree],[role=grid]," +
|
|
413
418
|
"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]")&&
|
|
414
419
|
(control.checkVisibility?control.checkVisibility({visibilityProperty:true}):control.getClientRects().length>0)}
|
|
@@ -510,11 +515,10 @@ fill(this.labels.itemsLoaded,{count:count.format(items.length)}));this.stateChan
|
|
|
510
515
|
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"||
|
|
511
516
|
typeof format.label!=="string"||!format.label.trim()||typeof format.extension!=="string"||!/^[a-z\d]{1,16}$/i.test(format.extension) ||
|
|
512
517
|
format.mime!==undefined&&typeof format.mime!=="string"||
|
|
518
|
+
format.values!==undefined&&!["raw","display"].includes(format.values)||
|
|
513
519
|
format.showInMenu!==undefined&&typeof format.showInMenu!=="boolean"||typeof format.write!=="function")
|
|
514
|
-
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})}
|
|
520
|
+
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})}
|
|
515
521
|
function registerExportFormat(format){format=exportFormat(format);EXPORT_FORMATS.set(format.id,format);return()=>EXPORT_FORMATS.get(format.id)===format&&EXPORT_FORMATS.delete(format.id)}
|
|
516
|
-
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))
|
|
517
|
-
.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}
|
|
518
522
|
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}
|
|
519
523
|
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}
|
|
520
524
|
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)}
|
|
@@ -526,18 +530,20 @@ fill(labels.searchFailed,{error:err&&err.message?err.message:err}))}}};const mov
|
|
|
526
530
|
if(active>=0){list.children[active].setAttribute("aria-selected","false")}
|
|
527
531
|
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=""}
|
|
528
532
|
close()}else{return}
|
|
529
|
-
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
|
|
530
|
-
|
|
533
|
+
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}
|
|
534
|
+
ready().then(value=>{if(!lifetime.signal.aborted&&isOpen())value?.[method]()}).catch(error=>{if(!lifetime.signal.aborted)(anchorElement.closest(".cx-host")||anchorElement)
|
|
535
|
+
.dispatchEvent(new CustomEvent("canopyx:error",{detail:{error},bubbles:true}))})};return{position:()=>invoke("position"),open:()=>invoke("open"),close:()=>flyout?.close(),dispose:()=>lifetime.abort()}}
|
|
536
|
+
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);
|
|
531
537
|
function registerPart(name,part){if(!partName(name)||!part||typeof part.version!=="string"||typeof part.install!=="function"||
|
|
532
538
|
part.requires!==undefined&&!(Array.isArray(part.requires)&&part.requires.every(partName)))throw new Error("CanopyX: invalid part.");PART_CODE.set(name,part)}
|
|
533
539
|
function definePart(View,name,label,context){const parts=View.prototype.parts||={};return parts[name]={name,label,context,parts}}
|
|
534
|
-
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:
|
|
540
|
+
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)}
|
|
535
541
|
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);
|
|
536
542
|
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")
|
|
537
543
|
.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")
|
|
538
544
|
.finally(()=>script.remove()).catch(error=>{part.scriptReady=null;throw error}));await Promise.all([style,code])}}
|
|
539
|
-
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})}
|
|
540
|
-
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,
|
|
545
|
+
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})}
|
|
546
|
+
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)}
|
|
541
547
|
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")
|
|
542
548
|
throw new Error("CanopyX: invalid state column.");seen.add(c.id);return{id:c.id,width:c.width,hidden:c.hidden}}
|
|
543
549
|
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})})}
|
|
@@ -588,7 +594,12 @@ w.content=nodeContent(w.h)});this.inlineOrderVersion=(this.inlineOrderVersion||0
|
|
|
588
594
|
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})}}
|
|
589
595
|
const navigation=navigationScope(this,this.dataLifetime.signal);let doc;try{doc=await navigation.wait(this.source.counts(wanted,this.sourceRequest(this.dataLifetime.signal)))}
|
|
590
596
|
finally{navigation.dispose()}
|
|
591
|
-
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}});
|
|
597
|
+
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(){}
|
|
598
|
+
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}
|
|
599
|
+
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}
|
|
600
|
+
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))}}
|
|
601
|
+
return mounted}
|
|
602
|
+
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.
|
|
592
603
|
}
|
|
593
604
|
this.linkChanged=this.dataLifetime.signal;return this.ready.then(()=>undefined,()=>undefined)}
|
|
594
605
|
return this.openDeepLink()};function sharedDensity(host){const value=host.closest("[data-hfx-density]")?.getAttribute("data-hfx-density");return value==="compact"||value==="comfortable"?value:null}
|
|
@@ -609,7 +620,7 @@ DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{linkFilteredBy:"Fi
|
|
|
609
620
|
linkMethods(TreeGrid);function linkGridPart(View){(Object.hasOwn(View.prototype,"parts")?View.prototype.parts:View.prototype.parts={}).link=LINK_PART}
|
|
610
621
|
function linkReady(grid,ready){return Promise.all([ready,grid.linkStart()]).then(([result])=>result)}
|
|
611
622
|
function linkApi(grid){const api={};for(const name of LINK_METHODS.concat("getExternalFilters","getHighlight"))api[name]=(...args)=>grid[name](...args);return api}
|
|
612
|
-
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)}
|
|
623
|
+
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)}
|
|
613
624
|
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}))}
|
|
614
625
|
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");
|
|
615
626
|
if(!options||typeof options!=="object"||Array.isArray(options)||Object.keys(options).some(key=>!HIGHLIGHT_KEYS.includes(key))||
|
|
@@ -631,16 +642,21 @@ function densityRowHeight(view,density){const shared=sharedDensity(view.host);if
|
|
|
631
642
|
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}
|
|
632
643
|
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))
|
|
633
644
|
this.setLayout(state.layout).catch(error=>this.emit("error",{error}));if(state.highlight!==undefined&&state.highlight!==!!this.highlightOn)
|
|
634
|
-
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))
|
|
635
|
-
.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()}});
|
|
645
|
+
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))
|
|
646
|
+
.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:'""'}
|
|
647
|
+
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"])
|
|
636
648
|
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}
|
|
637
649
|
function gridDateInputMoment(text,local){const match=/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{1,3}))?)?$/.exec(text);
|
|
638
650
|
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}
|
|
639
651
|
function gridFreeze(value){if(value&&typeof value==="object"&&!Object.isFrozen(value)){Object.values(value).forEach(gridFreeze);Object.freeze(value)}
|
|
640
652
|
return value}
|
|
641
|
-
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)}
|
|
653
|
+
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)}
|
|
642
654
|
function gridDecorateButton(button,icon,ghost){button.classList.add("cx-btn");if(ghost)button.classList.add("ghost");if(icon)button.prepend(gridIcon(icon));return button}
|
|
643
|
-
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)}
|
|
655
|
+
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)}
|
|
656
|
+
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})}
|
|
657
|
+
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]})}
|
|
658
|
+
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()}
|
|
659
|
+
return gridFreeze({kind:input.kind,timeZone,firstWeekday})}
|
|
644
660
|
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);
|
|
645
661
|
function gridColumnOptions(column,columns){if(column.className!=null&&!gridClassName(column.className)||
|
|
646
662
|
column.alignment!=null&&!["start","center","end"].includes(column.alignment)||
|
|
@@ -662,31 +678,41 @@ for(const key of keys)if(defaults[key]===undefined&&rule[key]!=null)defaults[key
|
|
|
662
678
|
function gridOptionsUsed(view,plugins=[]){return!!(view.footers?.length||view.columns.some(column=>column.orderData||column.sortComparator)||
|
|
663
679
|
plugins.some(plugin=>plugin.filterOperators||plugin.sortComparators))}
|
|
664
680
|
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)}
|
|
665
|
-
function gridPredicate(filter,columns,budget){if(++budget.predicates>64)throw new Error("CanopyX: at most 64 grid predicates.");
|
|
666
|
-
!(Object.hasOwn(GRID_FILTER_LABELS,filter.operator)
|
|
681
|
+
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"||
|
|
682
|
+
!(Object.hasOwn(GRID_FILTER_LABELS,filter.operator)||!bucket&&/^[a-z][a-zA-Z\d]{0,31}$/.test(filter.operator)) ||
|
|
667
683
|
!Array.isArray(values))
|
|
668
684
|
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)
|
|
669
685
|
throw new Error("CanopyX: invalid grid predicate arity.");if(["contains","startsWith"].includes(filter.operator)&&(column.kind!=="text"||values.some(value=>typeof value!=="string"))||
|
|
670
686
|
["gt","ge","lt","le"].includes(filter.operator)&&!["number","datetime"].includes(column.kind))
|
|
671
|
-
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)}
|
|
687
|
+
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)}
|
|
672
688
|
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)||
|
|
673
689
|
!group.filters.length&&!group.groups.length)
|
|
674
690
|
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)}
|
|
675
691
|
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)))&&
|
|
676
692
|
group.groups.every(child=>gridMatchesGroup(row,child,columns,matches));return group.filters.some(filter=>matches(row,filter,columns.get(filter.column)))||
|
|
677
693
|
group.groups.some(child=>gridMatchesGroup(row,child,columns,matches))}
|
|
678
|
-
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);
|
|
694
|
+
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);
|
|
679
695
|
}
|
|
680
|
-
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);
|
|
696
|
+
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)}
|
|
697
|
+
return foldText(text).includes(term)}))}
|
|
698
|
+
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)||
|
|
699
|
+
tuple.columns.length<1||tuple.columns.length>6||!Array.isArray(tuple.values)||
|
|
700
|
+
tuple.values.length<1||tuple.values.length>200||tuple.exclude!==undefined&&typeof tuple.exclude!=="boolean")
|
|
701
|
+
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)
|
|
702
|
+
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)}
|
|
703
|
+
return value})});return gridQueryRecord(tuple,{columns:fields,values,exclude:tuple.exclude??false},strict)})}
|
|
704
|
+
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})}
|
|
681
705
|
function gridQueryStart(query,patch){return{...query,...patch,offset:0,cursor:undefined,revision:undefined}}
|
|
682
706
|
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||
|
|
683
707
|
Object.keys(format).some(key=>!["minimumFractionDigits","maximumFractionDigits","prefix","suffix"].includes(key))||
|
|
684
708
|
[format.prefix,format.suffix].some(text=>text!==undefined&&(typeof text!=="string"||!text||text.length>16)))throw new Error("CanopyX: invalid number format.")}
|
|
685
|
-
function validateGridView(view){checkDocument(view,"grid");requireIdentifier(view.id,"grid ID");
|
|
686
|
-
throw new Error("CanopyX:
|
|
687
|
-
throw new Error("CanopyX:
|
|
688
|
-
throw new Error("CanopyX:
|
|
689
|
-
throw new Error("CanopyX:
|
|
709
|
+
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)
|
|
710
|
+
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)))
|
|
711
|
+
throw new Error("CanopyX: choose columnBands or columnGroups as an array.");if(view.rowIdentity!==undefined&&!["stableKey","ordinal"].includes(view.rowIdentity))
|
|
712
|
+
throw new Error("CanopyX: rowIdentity must be stableKey or ordinal.");if(view.rowIdentity==="ordinal"&&(view.live||view.record?.deepLink))
|
|
713
|
+
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)
|
|
714
|
+
throw new Error("CanopyX: a grid needs a label and columns.");if(!view.source||typeof view.source!=="object"||!["inline","host"].includes(view.source.kind))
|
|
715
|
+
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)}
|
|
690
716
|
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)||
|
|
691
717
|
column.headerDescription!==undefined&&typeof column.headerDescription!=="string"||
|
|
692
718
|
column.width!==undefined&&(!Number.isInteger(column.width)||column.width<40)||
|
|
@@ -694,45 +720,60 @@ column.width!==undefined&&(!Number.isInteger(column.width)||column.width<40)||
|
|
|
694
720
|
column.flex!==undefined&&!(typeof column.flex==="number"&&column.flex>=0&&column.flex<=100)||column.autoSize!==undefined&&!["content","header","none"].includes(column.autoSize)||
|
|
695
721
|
column.pinned!==undefined&&![null,"left","right"].includes(column.pinned)||column.priority!==undefined&&!gridPriority(column.priority)||
|
|
696
722
|
column.fixed&&column.width===undefined||column.tooltip!==undefined&&column.tooltip!=="auto"&&!view.columns.some(item=>item.id===column.tooltip))
|
|
697
|
-
throw new Error("CanopyX: invalid or duplicate grid column.");gridColumnOptions(column,view.columns);ids.add(column.id);
|
|
723
|
+
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))
|
|
724
|
+
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)}
|
|
698
725
|
if(column.aggregatable!==undefined&&(typeof column.aggregatable!=="boolean"||column.aggregatable&&column.kind!=="number")||
|
|
699
726
|
column.section!==undefined&&(typeof column.section!=="string"||!column.section.trim()||column.section.length>256))
|
|
700
727
|
throw new Error("CanopyX: invalid column aggregate or drawer section.")}
|
|
701
728
|
if(view.columns.every(column=>column.hidden)||view.columns.filter(column=>column.rowHeader).length>1)
|
|
702
729
|
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)||
|
|
703
|
-
["zebra","statusBar","autoHeight","rowAccents"].some(name=>view[name]!==undefined&&typeof view[name]!=="boolean")||
|
|
730
|
+
["zebra","statusBar","autoHeight","rowAccents","recordDrawerModal"].some(name=>view[name]!==undefined&&typeof view[name]!=="boolean")||
|
|
704
731
|
view.rowAction!==undefined&&!["none","details","link"].includes(view.rowAction)||
|
|
705
732
|
view.noun!==undefined&&(typeof view.noun!=="string"||!view.noun.trim()||view.noun.length>256)||
|
|
706
733
|
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))
|
|
707
734
|
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||
|
|
708
735
|
new Set(view.searchColumns).size!==view.searchColumns.length||view.searchColumns.some(id=>!ids.has(id))))
|
|
709
|
-
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.
|
|
710
|
-
|
|
711
|
-
view.
|
|
712
|
-
!Number.isInteger(view.
|
|
713
|
-
|
|
736
|
+
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)
|
|
737
|
+
throw new Error("CanopyX: pageSize must be 1 to 10000.");if(!["none","single","multiple"].includes(view.selection))
|
|
738
|
+
throw new Error("CanopyX: selection must be none, single or multiple.");if(view.autoRowHeight!==undefined&&typeof view.autoRowHeight!=="boolean")
|
|
739
|
+
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))
|
|
740
|
+
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))
|
|
741
|
+
throw new Error("CanopyX: estimatedRowHeight must be 20 to 64.");if(view.calculatedColumnsEditable!==undefined&&(typeof view.calculatedColumnsEditable!=="boolean"||view.calculatedColumnsEditable&&view.source.kind!=="inline"))
|
|
742
|
+
throw new Error("CanopyX: the calculated-column editor requires an inline grid.");if(view.source.navigation!==undefined&&!["offset","cursor"].includes(view.source.navigation))
|
|
714
743
|
throw new Error("CanopyX: invalid grid source navigation.");if(view.source.navigation==="cursor"&&(view.source.kind!=="host"||view.presentation!=="paged"))
|
|
715
744
|
throw new Error("CanopyX: cursors need a hosted paged grid.");if(view.presentation!==undefined&&!["continuous","paged"].includes(view.presentation)||
|
|
716
745
|
view.pageLength!==undefined&&(!Number.isInteger(view.pageLength)||view.pageLength<1||view.pageLength>10000))
|
|
717
|
-
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.")}
|
|
718
|
-
|
|
746
|
+
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.")}
|
|
747
|
+
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)}}
|
|
748
|
+
function gridQuery(view,input,strict){input=input||{};if(Array.isArray(input.tupleFilters)&&!input.tupleFilters.length){input={...input};delete input.tupleFilters}
|
|
749
|
+
const cursor=view.source.navigation==="cursor",type=cursor?"gridCursorQuery":"gridQuery";if(typeof input!=="object"||Array.isArray(input)||
|
|
719
750
|
input.protocol!==undefined&&input.protocol!==PROTOCOL||input.type!==undefined&&input.type!==type||
|
|
720
751
|
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)
|
|
721
752
|
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")
|
|
722
753
|
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.")}
|
|
723
|
-
if(input.searchMode!==undefined&&!["substring","words"].includes(input.searchMode))throw new Error("CanopyX: invalid grid search mode.");
|
|
754
|
+
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)}
|
|
755
|
+
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))
|
|
724
756
|
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)||
|
|
725
757
|
!["asc","desc"].includes(sort.direction))
|
|
726
758
|
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)}
|
|
727
759
|
query.sort=[...view.preSort.map(sort=>({column:sort.column,direction:sort.direction})),...query.sort.filter(sort=>!fixed.has(sort.column))]}
|
|
728
|
-
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)}
|
|
760
|
+
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)}
|
|
729
761
|
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)||
|
|
730
762
|
page.matched>page.total||page.offset>page.matched||!Array.isArray(page.items)||
|
|
731
763
|
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)}
|
|
732
764
|
function checkedGridPage(page,view,query,accepted){if(query.revision!==undefined&&page.revision!==query.revision||accepted&&
|
|
733
765
|
(page.revision!==accepted.revision||accepted.total!==undefined&&page.total!==accepted.total||
|
|
734
|
-
accepted.matched!==undefined&&page.matched!==accepted.matched))throw new Error("CanopyX: grid page snapshot changed.");validateGridRows(page.items,view.columns);return{...page,items
|
|
766
|
+
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}}
|
|
767
|
+
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}}
|
|
768
|
+
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{}
|
|
769
|
+
delete kept.sort;try{validateGridCell(kept,column);return kept}catch{}}
|
|
770
|
+
return text===undefined?value:{value,text}}
|
|
771
|
+
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)||
|
|
772
|
+
!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) => {
|
|
773
|
+
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.");
|
|
774
|
+
}}
|
|
735
775
|
function gridOrder(left,right){return left===right?0:left<right?-1:1}
|
|
776
|
+
function gridInlineRevision(view){return GRID_CALCULATIONS.revision?.(view)||"inline"}
|
|
736
777
|
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}
|
|
737
778
|
return id.length>0}
|
|
738
779
|
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}
|
|
@@ -740,13 +781,13 @@ function gridSortScalar(cell,column){return!column.valueOrder?.length&&isCellObj
|
|
|
740
781
|
function gridCompare(left,right,column,direction){return gridKeyCompare(gridSortScalar(left.cells[column.id],column),gridSortScalar(right.cells[column.id],column),column,direction)}
|
|
741
782
|
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}
|
|
742
783
|
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}
|
|
743
|
-
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)}
|
|
744
|
-
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)&&
|
|
745
|
-
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}
|
|
784
|
+
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)}
|
|
785
|
+
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)&&
|
|
786
|
+
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}
|
|
746
787
|
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}
|
|
747
788
|
return gridIdOrder(rows[a].id,rows[b].id)}).map(index=>rows[index])}
|
|
748
789
|
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}
|
|
749
|
-
return{protocol:PROTOCOL,type:"gridPage",view:view.id,revision:
|
|
790
|
+
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)}
|
|
750
791
|
return ordered.findIndex(row=>row.id===id)},row(id){return view.rows.find(row=>row.id===id)||null}}}
|
|
751
792
|
function gridCursor(cursor){requireIdentifier(cursor,"grid cursor");if(cursor.length>4096)throw new Error("CanopyX: cursor exceeds 4096 code units.")}
|
|
752
793
|
function validateGridCursorPage(page,view,query,accepted){checkDocument(page,"gridCursorPage");requireIdentifier(page.revision,"grid revision");if(page.view!==view.id||!validStructuralCount(page.offset)||
|
|
@@ -758,7 +799,7 @@ page.matched!==null&&next!==(end<page.matched)||page.total!==null&&next&&end>=pa
|
|
|
758
799
|
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)}
|
|
759
800
|
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)):
|
|
760
801
|
typeof scalar==="boolean"?"b:"+scalar:"n:"+scalar}
|
|
761
|
-
function validateGridFacetPage(input,view,request,snapshot){checkDocument(input,"gridFacetPage");const
|
|
802
|
+
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||
|
|
762
803
|
input.search!==request.search||input.includeCurrentFilter!==request.includeCurrentFilter||
|
|
763
804
|
!Number.isInteger(input.matched)||input.matched<0||input.matched>MAX_PROTOCOL_INTEGER||
|
|
764
805
|
!Number.isInteger(input.totalValues)||input.totalValues<0||input.totalValues>input.matched||
|
|
@@ -769,10 +810,10 @@ gridResultShape(gridColumnProjection(request.query,request.column))!==gridResult
|
|
|
769
810
|
(widened?input.matched<snapshot.matched:input.matched!==snapshot.matched))
|
|
770
811
|
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)
|
|
771
812
|
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)
|
|
772
|
-
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})}
|
|
813
|
+
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}:{})})}
|
|
773
814
|
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)
|
|
774
|
-
throw new Error("CanopyX:
|
|
775
|
-
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"))
|
|
815
|
+
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=[]}
|
|
816
|
+
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"))
|
|
776
817
|
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||
|
|
777
818
|
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?
|
|
778
819
|
this.view.source.kind==="inline"?0:300:this.view.searchDelayMs:options.searchDelayMs;if(!Number.isInteger(this.searchDelayMs)||this.searchDelayMs<0||this.searchDelayMs>5000)
|
|
@@ -790,10 +831,12 @@ async setQuery(input,paging){if(this.destroyed)throw new DOMException("Grid was
|
|
|
790
831
|
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;
|
|
791
832
|
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);
|
|
792
833
|
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)}}
|
|
793
|
-
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}
|
|
834
|
+
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}
|
|
794
835
|
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}
|
|
795
836
|
this.render();if(hadFocus){const element=this.focused&&this.rendered.get(this.focused);(element||this.grid).focus({preventScroll:true})}
|
|
796
|
-
this.host.setAttribute("data-cx-ready","true");if(this.searchInput&&!this.searchComposing&&this.searchTimer==null)this.searchInput.value=query.search;
|
|
837
|
+
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?
|
|
838
|
+
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("")}
|
|
839
|
+
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})}
|
|
797
840
|
throw error}}
|
|
798
841
|
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(()=>{})}
|
|
799
842
|
sort(sorts){return this.setQuery(gridQueryStart(this.queryForChange(true),{sort:sorts}))}
|
|
@@ -806,12 +849,12 @@ indexOf(row){if(!row||this.paged&&(row._ri<this.pageOffset||row._ri>=this.pageOf
|
|
|
806
849
|
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);
|
|
807
850
|
row.status=page&&page.error?"error":"loading";return row}
|
|
808
851
|
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}}
|
|
809
|
-
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})}
|
|
852
|
+
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})}
|
|
810
853
|
return measured}
|
|
811
854
|
observeDetail(){}
|
|
812
855
|
trimInactiveCache(){this.trimPages()}
|
|
813
856
|
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()}}
|
|
814
|
-
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()}
|
|
857
|
+
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()}
|
|
815
858
|
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()}
|
|
816
859
|
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")}}
|
|
817
860
|
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)
|
|
@@ -826,7 +869,7 @@ if(!this.rowCache.has(index))offsets.add(Math.floor(index / this.pageSize) * thi
|
|
|
826
869
|
if(this.focused?.status&&this.focused._ri>=0)offsets.add(Math.floor(this.focused._ri / this.pageSize) * this.pageSize);
|
|
827
870
|
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);
|
|
828
871
|
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))}
|
|
829
|
-
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])}
|
|
872
|
+
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])}
|
|
830
873
|
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:
|
|
831
874
|
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,
|
|
832
875
|
pageCount:this.paged?Math.ceil(count / this.pageLength) : count ? 1 : 0 } });
|
|
@@ -852,14 +895,16 @@ if(this.pendingNavigation!==navigation)return;if(this.destroyed||this.pendingQue
|
|
|
852
895
|
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&&
|
|
853
896
|
!this.destroyed&&!this.dataLifetime.signal.aborted&&epoch===this.queryEpoch&&!this.pendingQuery&&
|
|
854
897
|
this.pendingNavigation===navigation&&this.rowCache.get(index)===initial&&this.focused===initial&&
|
|
855
|
-
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))
|
|
898
|
+
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))
|
|
856
899
|
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})}
|
|
857
900
|
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)
|
|
858
|
-
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}
|
|
859
|
-
style.setProperty("--cx-min-h",(this.
|
|
860
|
-
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)}
|
|
901
|
+
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}
|
|
902
|
+
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)}
|
|
861
903
|
if(grid.style.opacity){this.render();grid.style.opacity=""}
|
|
862
|
-
else this.scheduleRender()};cancelAnimationFrame(resizeFrame);resizeFrame=requestAnimationFrame(resize)});this.resizeObserver.observe(grid);this.resizeObserver.observe(this.head)},
|
|
904
|
+
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)+
|
|
905
|
+
parseFloat(css.paddingTop)+ parseFloat(css.paddingBottom)+ child.offsetHeight-child.clientHeight;block=Math.max(block,parseFloat(css.minHeight)||0)}
|
|
906
|
+
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)
|
|
907
|
+
+ Math.max(0,count-1)*(parseFloat(host.rowGap)||0)},setLogicalTop(top){if(!this.hasGridSize()){this.hiddenScrollTop=top;return}
|
|
863
908
|
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(/ +/));
|
|
864
909
|
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)}
|
|
865
910
|
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)}
|
|
@@ -869,7 +914,7 @@ setClass(element,"cx-row" +(row.status?" cx-status cx-"+row.status:this.autoRows
|
|
|
869
914
|
}):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}
|
|
870
915
|
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}
|
|
871
916
|
else if(slot.missing){slot.cell.classList.remove("p-missing");slot.missing=false}
|
|
872
|
-
if(this.phone||slot.hiddenFields||slot.phonePrimary)this.paintHiddenFields?.(slot,row.h,column,this.columnIndex[i])}
|
|
917
|
+
if(this.phone||this.desktopHiddenFields||slot.hiddenFields||slot.phonePrimary)this.paintHiddenFields?.(slot,row.h,column,this.columnIndex[i])}
|
|
873
918
|
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}
|
|
874
919
|
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]}
|
|
875
920
|
return this}
|
|
@@ -890,9 +935,13 @@ this.total=this.rowOffsets.top(count);this.syncScrollRange(this.logicalTop());th
|
|
|
890
935
|
if(changed){this.total=this.rowOffsets.top(this.rowOffsets.count);this.syncScrollRange(rows.rowTop(anchor.index)+ anchor.offset)}
|
|
891
936
|
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));
|
|
892
937
|
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()}
|
|
893
|
-
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
|
|
894
|
-
|
|
895
|
-
const
|
|
938
|
+
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})}
|
|
939
|
+
if(/var\(/i.test(this.fitInline) && !this.fitBoundObserver) {
|
|
940
|
+
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})}
|
|
941
|
+
if(this.fitBoundObserver&&this.fitBoundParent!==this.host.parentElement){this.fitBoundObserver.disconnect();for(let parent=this.host;parent;parent=parent.parentElement)
|
|
942
|
+
this.fitBoundObserver.observe(parent,{attributes:true,attributeFilter:["style","class"]});this.fitBoundParent=this.host.parentElement}
|
|
943
|
+
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;
|
|
944
|
+
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}
|
|
896
945
|
function gridRank(column){const value=column.priority;return typeof value==="number"?value:value==="optional"?10000:value==="essential"||value===undefined&&column.identity?0:5000}
|
|
897
946
|
function gridLevel(column){const rank=gridRank(column);return rank<2?0:rank<10000?1:2}
|
|
898
947
|
function gridOptions(column){return column.presenter||{}}
|
|
@@ -929,19 +978,21 @@ if(total>width&&remaining>1){column.fitHidden=true;total -=size(column);remainin
|
|
|
929
978
|
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))
|
|
930
979
|
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"}
|
|
931
980
|
grid.columns=["left","none","right"].flatMap(side=>grid.columns.filter(column=>column.pin===side))}
|
|
932
|
-
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
|
|
933
|
-
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;
|
|
934
|
-
return Math.ceil(columnWidth(column,width))},fitShrink(cols,excess){const slack=cols.map(column=>Math.max(0,column.renderWidth-this.fitSize(column,
|
|
981
|
+
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)
|
|
982
|
+
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)))}
|
|
983
|
+
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;
|
|
935
984
|
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}
|
|
936
985
|
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})}
|
|
937
986
|
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}
|
|
938
987
|
queueMicrotask(()=>this.destroyed||this.applyWidths())}
|
|
939
|
-
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}
|
|
940
|
-
for(const column of this.columns){if(!column.fixed&&!column.sized)column.width=this.fitSize(column)}
|
|
941
|
-
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))}
|
|
988
|
+
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}
|
|
989
|
+
this.desktopDisclosureColumn=null;for(const column of this.columns){if(!column.fixed&&!column.sized)column.width=this.fitSize(column)}
|
|
990
|
+
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))}
|
|
942
991
|
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))+
|
|
943
992
|
(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+/);
|
|
944
|
-
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
|
|
993
|
+
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("→")+
|
|
994
|
+
text(changeCellText(change.after,spec,this.fmt))+ 12));sampled++;continue}
|
|
995
|
+
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)+
|
|
945
996
|
(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:
|
|
946
997
|
Math.min(kind==="longText"||kind==="tags"||kind==="list"||column.wrap?320:420,text(value))+
|
|
947
998
|
(kind==="progress"?80:kind==="tags"||kind==="list"?48:0));sampled++}
|
|
@@ -951,12 +1002,12 @@ return result};function validateGridHighlights(view){if(view.highlightRules===un
|
|
|
951
1002
|
rule.target!==undefined&&!["row","cell"].includes(rule.target)||rule.tone===undefined&&rule.className===undefined)
|
|
952
1003
|
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)}}
|
|
953
1004
|
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)}
|
|
954
|
-
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;
|
|
1005
|
+
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;
|
|
955
1006
|
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}
|
|
956
1007
|
if(rule){slot.cell.title=rule.label;slot.title=rule.label}
|
|
957
|
-
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(
|
|
958
|
-
["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()}
|
|
959
|
-
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||
|
|
1008
|
+
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"||
|
|
1009
|
+
["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()}
|
|
1010
|
+
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||
|
|
960
1011
|
options.limit!==undefined&&(!Number.isInteger(options.limit)||options.limit<1||options.limit>16)))invalid();if((options.minimum===undefined)!==(options.scaleMaximum===undefined)||options.minimum!==undefined&&
|
|
961
1012
|
(![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)||
|
|
962
1013
|
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();
|
|
@@ -971,16 +1022,17 @@ const value=raw.trim();try{const url=new URL(value,document.baseURI);if(["http:"
|
|
|
971
1022
|
}catch(_){/* Invalid destinations retain plain text. */ }
|
|
972
1023
|
return null}
|
|
973
1024
|
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}}
|
|
974
|
-
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)}}
|
|
1025
|
+
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)}}
|
|
975
1026
|
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})}
|
|
976
1027
|
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}
|
|
977
1028
|
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}
|
|
978
|
-
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}
|
|
1029
|
+
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}
|
|
979
1030
|
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}
|
|
980
1031
|
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)}
|
|
981
1032
|
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()}
|
|
982
1033
|
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}
|
|
983
|
-
|
|
1034
|
+
if(this.selectOnClick&&this.view.selection!=="none"){if(event.detail<2)this.selectClickedRow(row,event);return}
|
|
1035
|
+
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?
|
|
984
1036
|
link.dispatchEvent(new MouseEvent("click",{bubbles:true,cancelable:true,ctrlKey:event.ctrlKey,metaKey:event.metaKey,shiftKey:event.shiftKey})):
|
|
985
1037
|
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&&
|
|
986
1038
|
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)
|
|
@@ -1005,99 +1057,128 @@ if(event.key!=="F2")return false;GRID_DRAG.picked=gridDragItem(this,row);setText
|
|
|
1005
1057
|
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}
|
|
1006
1058
|
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. */ }
|
|
1007
1059
|
}}
|
|
1008
|
-
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)||
|
|
1060
|
+
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)||
|
|
1009
1061
|
!Number.isFinite(state.scrollTop)||state.scrollTop<0||state.scrollTop>MAX_PROTOCOL_INTEGER*this.rowH||!Array.isArray(state.selection))
|
|
1010
1062
|
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)
|
|
1011
1063
|
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")||
|
|
1012
1064
|
!Number.isInteger(paging.offset)||paging.offset<0||paging.offset>MAX_PROTOCOL_INTEGER||this.cursor&&paging.offset!==0)
|
|
1013
1065
|
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)}
|
|
1014
|
-
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||
|
|
1066
|
+
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||
|
|
1015
1067
|
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}))}))}
|
|
1016
1068
|
catch(_){/* A disabled or full storage leaves the current session usable. */ }
|
|
1017
|
-
}});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
|
|
1069
|
+
}});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)||
|
|
1070
|
+
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))}
|
|
1071
|
+
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()))
|
|
1018
1072
|
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";
|
|
1019
1073
|
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;
|
|
1020
1074
|
const parsed=typeof value==="string"&&/^\d{4}-\d\d-\d\dT\d\d:\d\d/.test(value) ? new Date(value) : null;
|
|
1021
1075
|
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.")}
|
|
1022
1076
|
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")}
|
|
1023
1077
|
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)}
|
|
1024
|
-
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})}
|
|
1078
|
+
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})}
|
|
1025
1079
|
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)}
|
|
1026
1080
|
return roots}
|
|
1027
|
-
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:"
|
|
1081
|
+
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")
|
|
1028
1082
|
throw new Error("CanopyX: invalid saved facet panel state.");if(this.view.facets||this.opts.facets)state.facets={open:facets.open}}
|
|
1029
1083
|
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}
|
|
1030
|
-
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())
|
|
1031
|
-
.catch(error=>{this.pager.hidden=this.opts.toolbar===false;this.emit("error",{error})})]).then(([result])=>result)};const
|
|
1032
|
-
|
|
1084
|
+
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())
|
|
1085
|
+
.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)}
|
|
1086
|
+
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")||
|
|
1087
|
+
!!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)=>
|
|
1033
1088
|
(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}
|
|
1034
|
-
class MatrixHost{}
|
|
1035
|
-
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
|
|
1036
|
-
|
|
1037
|
-
|
|
1089
|
+
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{}
|
|
1090
|
+
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)}
|
|
1091
|
+
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)}
|
|
1092
|
+
catch(error){showError(error);throw error}}
|
|
1093
|
+
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}
|
|
1094
|
+
const MATRIX_API={createMatrix};class TimeSliderHost{}
|
|
1095
|
+
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()}}
|
|
1096
|
+
class ChangesHost{}
|
|
1038
1097
|
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)}
|
|
1039
1098
|
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)}
|
|
1040
1099
|
catch(error){showError(error);throw error}}
|
|
1041
|
-
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}}
|
|
1042
|
-
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)})}}}
|
|
1043
|
-
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};
|
|
1100
|
+
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}}
|
|
1101
|
+
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)})}}}
|
|
1102
|
+
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)})}}
|
|
1103
|
+
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}
|
|
1104
|
+
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}
|
|
1044
1105
|
let keyboardInput=false;if(typeof document!=="undefined"){document.addEventListener("keydown",()=>{keyboardInput=true},true);document.addEventListener("pointerdown",()=>{keyboardInput=false},true)}
|
|
1045
1106
|
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}
|
|
1046
1107
|
function gridOption(value,text){const option=h("option");option.value=value;setText(option,text);return option}
|
|
1047
|
-
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
|
|
1108
|
+
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))}
|
|
1048
1109
|
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\})/){
|
|
1049
1110
|
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}))}
|
|
1050
|
-
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)
|
|
1051
|
-
this.
|
|
1052
|
-
if(
|
|
1111
|
+
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}
|
|
1112
|
+
this.setQuery(gridQueryStart(this.queryForChange(),{search:input.value})).then(()=>input.setCustomValidity(""))
|
|
1113
|
+
.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?
|
|
1114
|
+
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})})}
|
|
1115
|
+
(query.tupleFilters||[]).forEach((tuple,index)=>{const condition=tuple.columns.map(field=>this.columns.find(column=>column.id===field.column).title).join(", ")+
|
|
1116
|
+
(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)}
|
|
1053
1117
|
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})}
|
|
1054
1118
|
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(()=>{})))}
|
|
1055
1119
|
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())}}
|
|
1056
1120
|
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)}
|
|
1057
1121
|
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(()=>
|
|
1058
1122
|
this.runGridHook(signal=>this.opts.onChart({query:this.getQuery(),selection:this.getSelectionDescriptor?.()||
|
|
1059
|
-
{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));
|
|
1060
|
-
|
|
1061
|
-
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)}}
|
|
1123
|
+
{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")}
|
|
1124
|
+
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}
|
|
1125
|
+
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)}}
|
|
1062
1126
|
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()}
|
|
1063
1127
|
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))}
|
|
1064
|
-
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))}
|
|
1065
|
-
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};
|
|
1066
|
-
|
|
1128
|
+
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)))}
|
|
1129
|
+
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}
|
|
1130
|
+
return result}
|
|
1131
|
+
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}
|
|
1132
|
+
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})}}
|
|
1067
1133
|
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)}}
|
|
1068
1134
|
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)}
|
|
1069
1135
|
for(const column of[...grid.cols].reverse())if(column.effectivePin==="right"){column.pinOffset=end;end+=width(column)}
|
|
1070
1136
|
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)})}
|
|
1071
|
-
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)}
|
|
1137
|
+
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)}
|
|
1072
1138
|
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())}}
|
|
1073
|
-
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
|
|
1074
|
-
catch(error){this.setNotice(this.labels.gridLoadFailed,true);this.emit("error",{error})}}
|
|
1075
|
-
|
|
1139
|
+
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)}}
|
|
1140
|
+
catch(error){if(!this.destroyed&&this.pendingColumnMenu===request){this.setNotice(this.labels.gridLoadFailed,true);this.emit("error",{error})}}
|
|
1141
|
+
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}
|
|
1142
|
+
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}]}
|
|
1143
|
+
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))
|
|
1144
|
+
.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")}
|
|
1145
|
+
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)}
|
|
1076
1146
|
catch(error){resolutionError=error}
|
|
1077
|
-
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)})}
|
|
1078
|
-
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))
|
|
1147
|
+
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)})}
|
|
1148
|
+
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))
|
|
1079
1149
|
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||
|
|
1080
|
-
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:
|
|
1150
|
+
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:
|
|
1081
1151
|
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)||
|
|
1082
1152
|
index<(this.cursor?this.pageOffset:0)||index>=this.rowCount())
|
|
1083
1153
|
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)
|
|
1084
1154
|
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)})}
|
|
1085
1155
|
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)
|
|
1086
|
-
.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:"
|
|
1087
|
-
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)},
|
|
1088
|
-
|
|
1156
|
+
.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)}
|
|
1157
|
+
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)
|
|
1158
|
+
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):
|
|
1159
|
+
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}
|
|
1160
|
+
tokenColumns.add(slot);const inputs=[...text.matchAll(/\{([^{}]+)\}/g)].map(match => match[1]);
|
|
1161
|
+
if(inputs.some(id=>!this.columns.some(column=>column.id===id&&column.exportable!==false))){template[slot]=null;continue}
|
|
1162
|
+
for(const id of inputs){tokenColumns.add(id);if(!columns.includes(id))columns.push(id)}}
|
|
1163
|
+
capturedOptions={...capture,columns}}}
|
|
1164
|
+
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?
|
|
1165
|
+
this.exportFromMenu((options,onProgress)=>this.print({...options,onProgress})):this.print({scope:"filtered"}))
|
|
1166
|
+
.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())}}
|
|
1167
|
+
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}
|
|
1089
1168
|
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}
|
|
1090
|
-
const label=this.
|
|
1091
|
-
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}
|
|
1092
|
-
return}
|
|
1169
|
+
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}
|
|
1170
|
+
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}
|
|
1171
|
+
if(first&&count)slot.cell.appendChild(button);return}
|
|
1093
1172
|
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}
|
|
1094
1173
|
let line=slot.phoneLine;if(!line||line.parentNode!==slot.cell){line=h("span","cx-phone-fields");slot.cell.appendChild(line)}
|
|
1095
1174
|
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(""))})}}
|
|
1096
|
-
if(count&&button.parentNode!==
|
|
1097
|
-
{
|
|
1175
|
+
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")
|
|
1176
|
+
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&&
|
|
1177
|
+
{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"])
|
|
1098
1178
|
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)}}}
|
|
1099
|
-
GRID_REPORTING.api?.(api,grid);
|
|
1100
|
-
|
|
1179
|
+
GRID_REPORTING.api?.(api,grid);for(const name of["getViewIntent","validateViewIntent","previewViewIntent","applyViewIntent"])
|
|
1180
|
+
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}
|
|
1181
|
+
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})}
|
|
1101
1182
|
function checkBranch(){return{value:null,mask:0,children:new Map()}}
|
|
1102
1183
|
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)}
|
|
1103
1184
|
if(branch.value!==null)throw new Error("CanopyX: a checking path cannot have duplicate intents.");branch.value=item.checked}
|
|
@@ -1177,21 +1258,14 @@ cancelInteraction(){const operation=this.pendingInteraction;if(!operation)return
|
|
|
1177
1258
|
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()}
|
|
1178
1259
|
withInteractionReplacement(work){this.interactionReplacementDepth=(this.interactionReplacementDepth||0)+ 1;try{return work()}
|
|
1179
1260
|
finally{this.interactionReplacementDepth--;if(!this.interactionReplacementDepth){const controllers=this.interactionAbortQueue;this.interactionAbortQueue=null;if(controllers)for(const controller of controllers)controller.abort()}}}
|
|
1180
|
-
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()
|
|
1261
|
+
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()}
|
|
1262
|
+
buildToolbar(){super.buildToolbar();if(!(this.opts.onEdit&&this.columns.some(column=>column.editable)||
|
|
1263
|
+
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})})}
|
|
1181
1264
|
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))}})}
|
|
1182
|
-
openInteractionPanel(w,anchor,role,label){
|
|
1183
|
-
|
|
1184
|
-
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,"")}
|
|
1185
|
-
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}
|
|
1265
|
+
openInteractionPanel(w,anchor,role,label){return UI_IMPL.treeActions.openInteractionPanel.call(this,w,anchor,role,label)}
|
|
1266
|
+
closeInteractionPanel(restoreFocus=true){return UI_IMPL.treeActions?.closeInteractionPanel.call(this,restoreFocus)||null}
|
|
1186
1267
|
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})}}
|
|
1187
|
-
|
|
1188
|
-
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}
|
|
1189
|
-
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}
|
|
1190
|
-
next=Number(input.value)}
|
|
1191
|
-
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()}
|
|
1192
|
-
else if(event.key==="Enter"&&event.target===input){event.preventDefault();submit()}});this.placeInteractionPanel(row);input.focus();if(input.type!=="checkbox")input.select();return true}
|
|
1193
|
-
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}}
|
|
1194
|
-
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}}}}
|
|
1268
|
+
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}}}}
|
|
1195
1269
|
super.onKey(event)}
|
|
1196
1270
|
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))}
|
|
1197
1271
|
refreshCounts(ids){return this.withInteractionReplacement(()=>super.refreshCounts(ids))}
|
|
@@ -1206,19 +1280,9 @@ const result=super.reconcileList(parent,...args);focus=focus||this.interactionFo
|
|
|
1206
1280
|
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)})}
|
|
1207
1281
|
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})}
|
|
1208
1282
|
applyStatePresentation(state){const focus=this.closeInteractionPanel(false);this.moveNodes=null;this.cancelInteraction();const result=super.applyStatePresentation(state);this.restoreInteractionFocus(focus);return result}
|
|
1209
|
-
|
|
1210
|
-
markMove(
|
|
1211
|
-
|
|
1212
|
-
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}))}
|
|
1213
|
-
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)})}
|
|
1214
|
-
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"]])
|
|
1215
|
-
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}})}}
|
|
1216
|
-
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}
|
|
1217
|
-
else return;event.preventDefault();event.stopPropagation();if(target)target.focus()});this.placeInteractionPanel(anchor);buttons[0].focus();return true}
|
|
1218
|
-
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}
|
|
1219
|
-
if(event.key.toLowerCase()==="v"&&this.moveTarget(w)){event.preventDefault();event.stopPropagation();this.moveTo(w,"inside","keyboard");return}}
|
|
1220
|
-
super.onKey(event)}
|
|
1221
|
-
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}
|
|
1283
|
+
onKey(event){if(UI_IMPL.treeActions?.onKey.call(this,event,hostControl))return;super.onKey(event)}
|
|
1284
|
+
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())}}
|
|
1285
|
+
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}
|
|
1222
1286
|
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")}
|
|
1223
1287
|
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}
|
|
1224
1288
|
this.dragLease=null},{signal})}
|
|
@@ -1255,9 +1319,9 @@ fields.operator.appendChild(gridOption("between",this.labels.gridBetween));upper
|
|
|
1255
1319
|
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}
|
|
1256
1320
|
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]?
|
|
1257
1321
|
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)}
|
|
1258
|
-
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
|
|
1259
|
-
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],
|
|
1260
|
-
const items=[...buckets.values()].filter(item
|
|
1322
|
+
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:
|
|
1323
|
+
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})}
|
|
1324
|
+
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"}}
|
|
1261
1325
|
function reportingWidth(grid,column,width){const policy=reportingLayout(grid,column);return Math.min(policy.maximum,Math.max(policy.minimum,width))}
|
|
1262
1326
|
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)||
|
|
1263
1327
|
!Number.isInteger(policy.minimum)||policy.minimum<40||!Number.isInteger(policy.maximum)||
|
|
@@ -1283,7 +1347,7 @@ throw new Error("CanopyX: invalid responsive flag or minimum center width.");for
|
|
|
1283
1347
|
policy.alwaysVisible!==undefined&&typeof policy.alwaysVisible!=="boolean")
|
|
1284
1348
|
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}
|
|
1285
1349
|
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?
|
|
1286
|
-
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)=>
|
|
1350
|
+
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)=>
|
|
1287
1351
|
(reportingLayout(this,b).priority??MAX_PROTOCOL_INTEGER)-(reportingLayout(this,a).priority??MAX_PROTOCOL_INTEGER)||
|
|
1288
1352
|
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--}}
|
|
1289
1353
|
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])))
|
|
@@ -1294,7 +1358,7 @@ const PLACEMENT_HEADER=DataGrid.prototype.buildHeader,PLACEMENT_MOVE=DataGrid.pr
|
|
|
1294
1358
|
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})}
|
|
1295
1359
|
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")
|
|
1296
1360
|
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")
|
|
1297
|
-
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))||
|
|
1361
|
+
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))||
|
|
1298
1362
|
(value.version!==undefined||value.view!==undefined)&&(value.version!==1||value.view!==grid.view.id)||
|
|
1299
1363
|
!Array.isArray(value.views)||value.views.length>20)
|
|
1300
1364
|
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)})}
|
|
@@ -1313,7 +1377,7 @@ catch(error){this.viewSettingsIntent=intent;throw error}},removeSavedView(name){
|
|
|
1313
1377
|
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"])
|
|
1314
1378
|
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}}
|
|
1315
1379
|
Object.assign(DataGrid.prototype,{noteViewPreferenceIntent(){this.viewPreferenceIntent++},buildGridSearch(){VIEW_STORE_SEARCH.call(this);for(const event of["input","compositionstart","compositionend"])
|
|
1316
|
-
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)
|
|
1380
|
+
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)
|
|
1317
1381
|
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}
|
|
1318
1382
|
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()))}}
|
|
1319
1383
|
while(pending){try{await pending;if(pending===this.viewQueryPromise)return this.getQuery()}
|
|
@@ -1338,35 +1402,28 @@ this.selectionScopeKey=key;this.selectionRevision=detail.revision}
|
|
|
1338
1402
|
if(name==="select")detail={...detail,selection:this.getSelectionDescriptor()};if(name==="select"||name==="query"||name==="error")this.syncSelectionControls?.()}
|
|
1339
1403
|
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))
|
|
1340
1404
|
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()):
|
|
1341
|
-
this.selectRange(bounds.first,bounds.last,{...options,selected,additive:true})},async selectRange(start,end,options={}){this.requireReportingSelection(true);
|
|
1342
|
-
typeof selected!=="boolean"||typeof additive!=="boolean"||options.signal!==undefined&&!(options.signal instanceof AbortSignal))
|
|
1343
|
-
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))
|
|
1344
|
-
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) {
|
|
1345
|
-
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)}}
|
|
1346
|
-
gridOperationActive(this,controller.signal);if(epoch!==this.queryEpoch||ids.length!==last-first+1)
|
|
1347
|
-
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])}
|
|
1348
|
-
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}
|
|
1405
|
+
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}
|
|
1349
1406
|
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))
|
|
1350
1407
|
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&&
|
|
1351
1408
|
selection.matched-selection.excludedIds.length>maxRows)
|
|
1352
1409
|
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"])
|
|
1353
|
-
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}}
|
|
1410
|
+
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}}
|
|
1354
1411
|
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)}):
|
|
1355
|
-
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");
|
|
1412
|
+
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)}})}
|
|
1356
1413
|
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?
|
|
1357
1414
|
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&&
|
|
1358
1415
|
!event.target.closest("a,button,input,select,textarea,[contenteditable]")&&(event.shiftKey||event.ctrlKey||event.metaKey)){this.setFocus(row,true);this.reportingSelectionIntent(row,event.shiftKey||!this.reportingRowSelected(row),event.shiftKey,event.ctrlKey||event.metaKey);return}
|
|
1359
1416
|
try{SELECTION_CONTROLS_BASE.onClick.call(this,event)}
|
|
1360
1417
|
catch(error){if(!this.view.reporting?.selectionControls)throw error;this.runReportingSelection(()=>{throw error});return}
|
|
1361
|
-
if(this.view.reporting?.selectionControls&&row&&!row.status&&!event.defaultPrevented&&
|
|
1418
|
+
if(!this.selectOnClick&&this.view.reporting?.selectionControls&&row&&!row.status&&!event.defaultPrevented&&
|
|
1362
1419
|
!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&&
|
|
1363
1420
|
!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}
|
|
1364
|
-
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:"
|
|
1421
|
+
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}
|
|
1365
1422
|
const reportingRowsUsed=grid=>reportingSummaryEnabled(grid.view)||!!grid.view.reporting?.detail||!!grid.opts.detail;function reportingGroupKey(value){return JSON.stringify([typeof value,value])}
|
|
1366
1423
|
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)||
|
|
1367
|
-
Object.keys(level).some(key=>!["column","direction","aggregate"].includes(key))||
|
|
1424
|
+
Object.keys(level).some(key=>!["column","direction","aggregate","bucket"].includes(key))||
|
|
1368
1425
|
level.aggregate!==undefined&&!aggregates.some(aggregate=>aggregate.id===level.aggregate))
|
|
1369
|
-
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})})})}
|
|
1426
|
+
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)})})})}
|
|
1370
1427
|
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)||
|
|
1371
1428
|
!["asc","desc"].includes(grouping.direction)||
|
|
1372
1429
|
!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))
|
|
@@ -1385,47 +1442,54 @@ detail.label!==undefined&&(typeof detail.label!=="string"||!detail.label.trim()|
|
|
|
1385
1442
|
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)]})}
|
|
1386
1443
|
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}}
|
|
1387
1444
|
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)}
|
|
1388
|
-
return{protocol:PROTOCOL,type:"gridPage",view:view.id,revision:
|
|
1445
|
+
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}]:
|
|
1389
1446
|
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)}
|
|
1390
1447
|
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")}
|
|
1391
1448
|
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)?
|
|
1392
1449
|
query.groupBy.filter(level=>level&&this.columns.some(column=>column.id===level.column&&column.sortable)).slice(0,GROUP_LEVELS)
|
|
1393
1450
|
.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))
|
|
1394
1451
|
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}
|
|
1395
|
-
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";
|
|
1396
|
-
{
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
const
|
|
1405
|
-
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)}
|
|
1406
|
-
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}
|
|
1452
|
+
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)}
|
|
1453
|
+
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, "%");
|
|
1454
|
+
const format=aggregate.format||(!reportingCounting(aggregate.operation)&&column?.format);return mode==="rank"?numberText(value,grid.lang,{minimumFractionDigits:0,maximumFractionDigits:0}):
|
|
1455
|
+
format?numberText(value,grid.lang,format):numberText(value,grid.lang,{minimumFractionDigits:reportingCounting(aggregate.operation)?0:2,maximumFractionDigits:reportingCounting(aggregate.operation)?0:2})}
|
|
1456
|
+
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(/^% /, ""));
|
|
1457
|
+
titles.push(reportingAggregateName(grid,aggregate)+ ": "+shown +(caption?" ("+caption+")":""))}
|
|
1458
|
+
return{text:text.join(" · "),title:titles.join(" · "),caption:""}}
|
|
1459
|
+
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)}
|
|
1460
|
+
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)}
|
|
1461
|
+
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}
|
|
1407
1462
|
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")}}
|
|
1408
1463
|
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())) {
|
|
1409
1464
|
const safe=gridPresenterLink(target);if(safe&&/^(https?:|mailto:)/i.test(safe)) link = { target: safe, ...(object && cell.title ? { tooltip: cell.title } : {}) };
|
|
1410
1465
|
else diagnostics.push("unsafe-link")}else if(target)diagnostics.push("relative-link")}
|
|
1411
1466
|
const mark=matches.get(column.id),tone=mark?.tone??columnValueTone(column,cell)??(presenter?.kind==="boolean"&&value===true&&presenter.inverted?"danger":
|
|
1412
1467
|
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)]}:{})}}
|
|
1413
|
-
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}
|
|
1468
|
+
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}
|
|
1414
1469
|
function reportingFileName(value){if(typeof value!=="string"||!value||value.length>100||value!==value.trim()||
|
|
1415
1470
|
/[\\/:*?"<>|\x00-\x1f\x7f-\x9f\u2028\u2029\u202a-\u202e\u2066-\u2069]/u.test(value) ||
|
|
1416
1471
|
/^\.|[. ]$/.test(value) || /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(value))
|
|
1417
1472
|
throw new Error("CanopyX: export file name must be a safe basename of at most 100 characters.");requireIdentifier(value,"export file name");return value}
|
|
1418
1473
|
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||
|
|
1419
1474
|
total!==null&&count>total)throw new Error("CanopyX: invalid export progress.");completed=count;phase=next;callback?.(Object.freeze({phase,completed,total}));return completed}}
|
|
1420
|
-
async function reportingExportSnapshot(grid,exported,maxRows,signal,progress){
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
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)
|
|
1475
|
+
async function reportingExportSnapshot(grid,exported,maxRows,signal,progress){await loadExportPart();return EXPORT_IMPL.snapshot(grid,exported,maxRows,signal,progress)}
|
|
1476
|
+
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"))
|
|
1477
|
+
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)
|
|
1424
1478
|
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.")}}
|
|
1425
|
-
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));
|
|
1426
|
-
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)}
|
|
1427
|
-
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)
|
|
1428
|
-
|
|
1479
|
+
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));
|
|
1480
|
+
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)}
|
|
1481
|
+
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)||
|
|
1482
|
+
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||
|
|
1483
|
+
values.some(id=>!view.reporting.aggregates?.some(aggregate=>aggregate.id===id))||
|
|
1484
|
+
!Number.isInteger(maxColumns)||maxColumns<1||maxColumns>200||
|
|
1485
|
+
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))||
|
|
1486
|
+
!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}})})}
|
|
1487
|
+
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}
|
|
1488
|
+
function pivotIntent(grid){if(grid.pivotState===undefined)grid.pivotState=pivotDefaultState(grid.view);return grid.pivotState}
|
|
1489
|
+
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)}}
|
|
1490
|
+
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),{}))}
|
|
1491
|
+
catch(error){this.pivotState=previous;this.render();throw error}
|
|
1492
|
+
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)}
|
|
1429
1493
|
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)
|
|
1430
1494
|
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})}
|
|
1431
1495
|
catch(error){body.drawn=false;body.replaceChildren();this.emit("error",{error})}}
|
|
@@ -1437,7 +1501,8 @@ const FLOW_ASYNC_METHODS=["next","back","goTo","setState","validate","submit","r
|
|
|
1437
1501
|
.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()}
|
|
1438
1502
|
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}
|
|
1439
1503
|
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.")}
|
|
1440
|
-
|
|
1504
|
+
if(options.recordDrawerModal!==undefined&&typeof options.recordDrawerModal!=="boolean")
|
|
1505
|
+
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"])
|
|
1441
1506
|
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);
|
|
1442
1507
|
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}
|
|
1443
1508
|
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.")}
|
|
@@ -1450,5 +1515,5 @@ watcher.observe(host)}
|
|
|
1450
1515
|
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}
|
|
1451
1516
|
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}}
|
|
1452
1517
|
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}
|
|
1453
|
-
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}
|
|
1454
|
-
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:{}));if(typeof document!=="undefined"){const navigation=global.performance&&performance.getEntriesByType?performance.getEntriesByType("navigation")[0]:null;if(document.readyState==="loading"||(document.readyState==="interactive"&&navigation&&navigation.domContentLoadedEventStart===0)){document.addEventListener("DOMContentLoaded",()=>mount(document,true))}else{mount(document,true)}}})(runtimeHost);const CanopyX=runtimeHost.CanopyX;const{protocol,createTreeGrid,createDataGrid,createCalendar,createFlow,createMatrix,createDiff,createChangeList,createPropagation,enhance,grid,configure,unconfigure,mount,sources,badge,labels,registerExportFormat,use,registerPart,setNonce}=CanopyX;export{protocol,createTreeGrid,createDataGrid,createCalendar,createFlow,createMatrix,createDiff,createChangeList,createPropagation,enhance,grid,configure,unconfigure,mount,sources,badge,labels,registerExportFormat,use,registerPart,setNonce};export default CanopyX;
|
|
1518
|
+
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}
|
|
1519
|
+
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));if(typeof document!=="undefined"){const navigation=global.performance&&performance.getEntriesByType?performance.getEntriesByType("navigation")[0]:null;if(document.readyState==="loading"||(document.readyState==="interactive"&&navigation&&navigation.domContentLoadedEventStart===0)){document.addEventListener("DOMContentLoaded",()=>mount(document,true))}else{mount(document,true)}}})(runtimeHost);const CanopyX=runtimeHost.CanopyX;const{protocol,createTreeGrid,createDataGrid,createCalendar,createFlow,createMatrix,createDiff,createChangeList,createPropagation,enhance,grid,configure,unconfigure,mount,sources,badge,labels,registerExportFormat,use,registerPart,setNonce, mountUi,contextMenu,tabs,stepper,dialog,drawer,popover,tooltip,menu}=CanopyX;export{protocol,createTreeGrid,createDataGrid,createCalendar,createFlow,createMatrix,createDiff,createChangeList,createPropagation,enhance,grid,configure,unconfigure,mount,sources,badge,labels,registerExportFormat,use,registerPart,setNonce, mountUi,contextMenu,tabs,stepper,dialog,drawer,popover,tooltip,menu};export default CanopyX;
|