@evotecit/canopyx 0.3.3 → 0.3.4
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-data.js +5 -4
- package/CanopyX.Html/Assets/canopyx-grid-module.mjs +31 -15
- package/CanopyX.Html/Assets/canopyx-grid.js +31 -15
- package/CanopyX.Html/Assets/canopyx-grouping.js +2 -2
- package/CanopyX.Html/Assets/canopyx-interactive.js +4 -4
- package/CanopyX.Html/Assets/canopyx-labels-de.js +33 -0
- package/CanopyX.Html/Assets/canopyx-labels-es.js +33 -0
- package/CanopyX.Html/Assets/canopyx-labels-fr.js +33 -0
- package/CanopyX.Html/Assets/canopyx-labels-it.js +33 -0
- package/CanopyX.Html/Assets/canopyx-labels-pl.js +33 -0
- package/CanopyX.Html/Assets/canopyx-live.js +2 -2
- package/CanopyX.Html/Assets/canopyx-matrix.css +153 -0
- package/CanopyX.Html/Assets/canopyx-matrix.js +153 -0
- package/CanopyX.Html/Assets/canopyx-module.mjs +4 -4
- package/CanopyX.Html/Assets/canopyx-reporting-module.mjs +32 -16
- package/CanopyX.Html/Assets/canopyx-reporting.js +32 -16
- package/CanopyX.Html/Assets/canopyx-tree-tools.js +3 -2
- package/CanopyX.Html/Assets/canopyx.js +4 -4
- package/JavaScript/grid.d.ts +8 -5
- package/JavaScript/locales/de.mjs +33 -0
- package/JavaScript/locales/es.mjs +33 -0
- package/JavaScript/locales/fr.mjs +33 -0
- package/JavaScript/locales/it.mjs +33 -0
- package/JavaScript/locales/pl.mjs +33 -0
- package/JavaScript/matrix.d.ts +160 -0
- package/README.md +3 -2
- package/package.json +5 -1
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
(function(global){"use strict";const installed=new WeakSet();const part={version:"a02d6881b70019b4",install(context){if(installed.has(context.MATRIX_IMPL))return;const{MATRIX_IMPL,h,setText,fill,foldText,numberFormat,gridFreeze,toneOf,applyTone,toneColors,AxisWindow,PROTOCOL}=context;const MATRIX_LABELS={matrixRows:"Rows",matrixColumns:"Columns",matrixSearch:"Find rows and columns",matrixSortBy:"Sort by",matrixGroupBy:"Group by",matrixByLabel:"Name",matrixByTotal:"Total",matrixAsListed:"As listed",matrixNoGrouping:"No groups",matrixDescending:"Descending",matrixOnlyProblems:"Only problems",matrixHideEmpty:"Hide empty rows and columns",matrixSize:"{rows} × {columns}",matrixShown:"Rows: {rows} of {totalRows}, columns: {columns} of {totalColumns}",matrixAttention:"Need attention: {count}",matrixLegend:"States",matrixFilter:"Filter",matrixClearFilters:"Clear filters",matrixNoMatches:"No rows or columns match.",matrixSelf:"Same item",matrixNoData:"No data",matrixCell:"{row} and {column}: {state}",matrixTotal:"Total",matrixItemTotal:"{item}: {count}",matrixExpandGroup:"Expand {group}",matrixCollapseGroup:"Collapse {group}",matrixGroupCount:"Items: {count}",matrixDetails:"Details",matrixClose:"Close",matrixValue:"Value",matrixState:"State",matrixAllClear:"Nothing needs attention",matrixShowMore:"Show more"};function matrixLabels(...sources){const labels={...MATRIX_LABELS};for(const source of sources){if(source==null)continue;if(typeof source!=="object"||Array.isArray(source))matrixFail("labels must be an object of strings");for(const[name,text]of Object.entries(source)){if(!Object.hasOwn(labels,name)){if(/^matrix[A-Z]/.test(name)) matrixFail("there is no matrix label '" + name + "'"); continue; }
|
|
2
|
+
if(typeof text!=="string"||!/\S/.test(text)) matrixFail("label '" + name + "' must be a string with visible text");
|
|
3
|
+
labels[name]=text}}
|
|
4
|
+
return Object.freeze(labels)}
|
|
5
|
+
function matrixTemplate(text){const parts=text.split(/\{(\w+)\}/);
|
|
6
|
+
return values=>{let result=parts[0];for(let i=1;i<parts.length;i +=2)result +=(values[parts[i]]??"{"+parts[i]+ "}")+ parts[i+1];return result}}
|
|
7
|
+
function matrixFail(message){throw new Error("CanopyX: the matrix document is not valid: "+message+".")}
|
|
8
|
+
const MATRIX_DIGITS="0123456789abcdefghijklmnopqrstuvwxyz",MATRIX_NONE=255,MATRIX_SELF=254;const MATRIX_SYMBOLS=["check","cross","warning","info","dot","dash","question","clock","pause"];const MATRIX_KEYS=["protocol","type","id","label","rows","columns","square","columnTitle","diagonal","states","cells","notes","totals","attentionOnly","hideEmpty","cellWidth","density","details","phone","legend","autoHeight","labels"];const matrixObject=value=>value!==null&&typeof value==="object"&&!Array.isArray(value);function matrixKeys(value,keys,what){if(!matrixObject(value))matrixFail(what+" must be an object");for(const key of Object.keys(value))if(!keys.includes(key))matrixFail(what+" has the unknown property '"+key+"'");return value}
|
|
9
|
+
function matrixText(value,what,max,optional){if(value===undefined&&optional)return null;if(typeof value!=="string"||!/\S/.test(value) || value.length > max) matrixFail(what + " must be visible text of at most " + max + " characters");
|
|
10
|
+
return value}
|
|
11
|
+
function matrixId(value,what){if(typeof value!=="string"||!value.length||value.length>256||/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:^|[^\uD800-\uDBFF])[\uDC00-\uDFFF]/.test(value)) matrixFail(what + " must be a nonempty id of at most 256 characters");
|
|
12
|
+
return value}
|
|
13
|
+
function matrixMoment(value){return typeof value==="string"?Date.parse(value.includes("T")&&!/(?:Z|[+-]\d\d:\d\d)$/.test(value) ? value + "Z" : value) : NaN;
|
|
14
|
+
}
|
|
15
|
+
function matrixHref(value){if(typeof value!=="string"||!value.length||value.length>2048||value.startsWith("//")||/[\u0000- \u007f\\]/.test(value)) return false;
|
|
16
|
+
const colon=value.indexOf(":"),boundary=value.search(/[/?#]/);
|
|
17
|
+
return colon<0||boundary>=0&&boundary<colon||/^(?:https?|mailto)$/i.test(value.slice(0, colon));
|
|
18
|
+
}
|
|
19
|
+
function matrixAxis(input,what){const axis=matrixKeys(input,["title","noun","attributes","items","groupBy","sort"],what);const attributes=[],byId=new Map();if(axis.attributes!==undefined&&(!Array.isArray(axis.attributes)||axis.attributes.length>32))matrixFail(what+" has at most 32 attributes");for(const entry of axis.attributes||[]){const attribute=matrixKeys(entry,["id","title","kind","facet"],what+" attribute");const id=matrixId(attribute.id,"an attribute id");if(id[0]==="#"||byId.has(id))matrixFail("the attribute '"+id+"' must be unique and not start with #");const kind=attribute.kind===undefined?"text":attribute.kind;if(!["text","number","datetime"].includes(kind)||attribute.facet!==undefined&&typeof attribute.facet!=="boolean")matrixFail("the attribute '"+id+"' has an unknown kind or facet");const item=Object.freeze({id,title:matrixText(attribute.title,"the title of attribute '"+id+"'",200),kind,facet:attribute.facet===true});attributes.push(item);byId.set(id,item)}
|
|
20
|
+
if(!Array.isArray(axis.items)||!axis.items.length||axis.items.length>10000)matrixFail(what+" needs 1 to 10000 items");const ids=new Map(),items=axis.items.map((entry,index)=>{const item=matrixKeys(entry,["id","label","secondary","href","values"],what+" item");const id=matrixId(item.id,"an item id");if(ids.has(id))matrixFail("the item '"+id+"' appears twice on "+what);if(item.href!==undefined&&!matrixHref(item.href))matrixFail("the link of '"+id+"' is not an http, https, mailto or relative address");const values=Object.create(null);if(item.values!==undefined){if(!matrixObject(item.values))matrixFail("the values of '"+id+"' must be an object");for(const[key,raw]of Object.entries(item.values)){const attribute=byId.get(key);if(!attribute)matrixFail("'"+id+"' has a value for the undeclared attribute '"+key+"'");const cell=matrixObject(raw)?matrixKeys(raw,["value","text"],"a value"):{value:raw};const value=cell.value??null;const fits=value===null||(attribute.kind==="number"?typeof value==="number"&&Number.isFinite(value):attribute.kind==="datetime"?
|
|
21
|
+
typeof value==="string"&&/^\d{4}-\d\d-\d\d(?:T\d\d:\d\d(?::\d\d(?:\.\d{1,7})?)?(?:Z|[+-]\d\d:\d\d)?)?$/.test(value) && Number.isFinite(matrixMoment(value)) : typeof value === "string" || typeof value === "boolean");
|
|
22
|
+
if(!fits||cell.text!==undefined&&typeof cell.text!=="string")matrixFail("the value of '"+key+"' of '"+id+"' does not fit its kind");values[key]={value,text:cell.text??null}}}
|
|
23
|
+
const result=Object.freeze({id,index,label:matrixText(item.label,"the label of '"+id+"'",200),secondary:matrixText(item.secondary,"the secondary text of '"+id+"'",200,true),href:item.href??null,values:Object.freeze(values)});ids.set(id,result);return result});if(axis.groupBy!==undefined&&!byId.has(axis.groupBy))matrixFail(what+" groups by an undeclared attribute");let sort=null;if(axis.sort!==undefined){const value=matrixKeys(axis.sort,["by","direction"],what+" sort");if(!["#label","#total"].includes(value.by)&&!byId.has(value.by)||![undefined,"asc","desc"].includes(value.direction))matrixFail(what+" sorts by an unknown key");sort={by:value.by,direction:value.direction||"asc"}}
|
|
24
|
+
if(axis.noun!==undefined)matrixText(axis.noun,what+" noun",64);return Object.freeze({title:matrixText(axis.title,what+" title",200,true),noun:axis.noun??null,attributes:Object.freeze(attributes),attributeById:byId,items:Object.freeze(items),byId:ids,groupBy:axis.groupBy??null,sort})}
|
|
25
|
+
function matrixDocument(input){const doc=matrixKeys(input,MATRIX_KEYS,"the document");if(doc.protocol!==PROTOCOL||doc.type!=="matrix")matrixFail("it needs protocol "+PROTOCOL+" and type \"matrix\"");const id=matrixId(doc.id,"the matrix id"),label=matrixText(doc.label,"the label",200);if(doc.square!==undefined&&typeof doc.square!=="boolean")matrixFail("square must be true or false");const square=doc.square===true,rows=matrixAxis(doc.rows,"rows");if(square&&doc.columns!==undefined||!square&&(doc.columns===undefined||doc.columnTitle!==undefined))matrixFail("a matrix has columns, or is square and has none (and may have a columnTitle)");const columns=square?rows:matrixAxis(doc.columns,"columns");if(![undefined,"self","data"].includes(doc.diagonal)||doc.diagonal!==undefined&&!square)matrixFail("only a square matrix has a diagonal, self or data");if(rows.items.length*columns.items.length>1000000)matrixFail("a matrix holds at most 1000000 cells");if(!Array.isArray(doc.states)||!doc.states.length||doc.states.length>36)matrixFail("it needs 1 to 36 states");const stateIds=new Set(),states=doc.states.map((entry,index)=>{const state=matrixKeys(entry,["id","label","tone","symbol","attention"],"a state");if(typeof state.id!=="string"||!/^[A-Za-z][\w-]{0,63}$/.test(state.id) || stateIds.has(state.id)) matrixFail("states need unique ids");
|
|
26
|
+
stateIds.add(state.id);if(state.tone!==undefined&&(typeof state.tone!=="string"||!/^[a-z][a-z0-9-]{0,31}$/.test(state.tone) || /-bg$/.test(state.tone))) matrixFail("the state '" + state.id + "' has an invalid tone");
|
|
27
|
+
if(!MATRIX_SYMBOLS.includes(state.symbol)||state.attention!==undefined&&typeof state.attention!=="boolean")matrixFail("the state '"+state.id+"' needs one of the symbols");return Object.freeze({id:state.id,index,label:matrixText(state.label,"the label of state '"+state.id+"'",200),tone:state.tone||null,symbol:state.symbol,attention:state.attention===true})});const R=rows.items.length,C=columns.items.length,grid=new Uint8Array(R*C).fill(MATRIX_NONE);if(!Array.isArray(doc.cells)||doc.cells.length!==R)matrixFail("cells needs one string per row");doc.cells.forEach((line,r)=>{if(typeof line!=="string"||line.length!==C)matrixFail("each cells string needs one character per column");for(let c=0;c<C;c++){const ch=line.charCodeAt(c);if(ch===46)continue;const index=MATRIX_DIGITS.indexOf(line[c]);if(index<0||index>=states.length)matrixFail("cells name an undeclared state");grid[r*C+c]=index}});const diagonal=doc.diagonal||"self";if(square&&diagonal==="self")for(let r=0;r<R;r++)grid[r*C+r]=MATRIX_SELF;const notes=new Map();if(doc.notes!==undefined&&!Array.isArray(doc.notes))matrixFail("notes must be a list");for(const note of doc.notes||[]){if(!Array.isArray(note)||note.length<3||note.length>6||!Number.isInteger(note[0])||!Number.isInteger(note[1])||note[0]<0||note[1]<0||note[0]>=R||note[1]>=C)matrixFail("a note names no cell");const at=note[0]*C+note[1];if(grid[at]>=MATRIX_SELF||notes.has(at))matrixFail("a note names a cell without a state, or twice");const[,,value=null,text=null,title=null,details=null]=note;if(value!==null&&(typeof value!=="number"||!Number.isFinite(value)||Math.abs(value)>Number.MAX_SAFE_INTEGER))matrixFail("a note value must be a finite number");if(text!==null)matrixText(text,"a note text",64);if(title!==null)matrixText(title,"a note title",1000);if(details!==null&&(!Array.isArray(details)||details.length>16||details.some(pair=>!Array.isArray(pair)||pair.length!==2||
|
|
28
|
+
typeof pair[0]!=="string"||!/\S/.test(pair[0]) || pair[0].length > 200 || typeof pair[1] !== "string" || pair[1].length > 1000))) matrixFail("note details are up to 16 label and value pairs");
|
|
29
|
+
notes.set(at,Object.freeze({value,text,title,details:details?Object.freeze(details.map(pair=>Object.freeze([...pair]))):null}))}
|
|
30
|
+
const totals=doc.totals===undefined?{}:matrixKeys(doc.totals,["states","rows","columns"],"totals");if(totals.states!==undefined&&(!Array.isArray(totals.states)||totals.states.some(state=>!stateIds.has(state))))matrixFail("totals count undeclared states");if([totals.rows,totals.columns,doc.attentionOnly,doc.hideEmpty,doc.legend,doc.autoHeight].some(value=>value!==undefined&&typeof value!=="boolean"))matrixFail("totals, attentionOnly, hideEmpty, legend and autoHeight take true or false");if(doc.cellWidth!==undefined&&(!Number.isInteger(doc.cellWidth)||doc.cellWidth<24||doc.cellWidth>120))matrixFail("cellWidth is 24 to 120 pixels");if(![undefined,"compact","normal","comfortable"].includes(doc.density)||![undefined,"dialog","host"].includes(doc.details)||![undefined,"scroll","cards"].includes(doc.phone))matrixFail("density, details or phone has an unknown value");const counted=totals.states?states.filter(state=>totals.states.includes(state.id)):states.filter(state=>state.attention);return Object.freeze({id,label,square,diagonal,rows,columns,columnTitle:square?matrixText(doc.columnTitle,"the column title",200,true)||rows.title:columns.title,states:Object.freeze(states),stateById:new Map(states.map(state=>[state.id,state])),grid,notes,total:new Uint8Array(states.map(state=>counted.includes(state)?1:0)),attention:new Uint8Array(states.map(state=>state.attention?1:0)),rowTotals:totals.rows!==false,columnTotals:totals.columns!==false,attentionOnly:doc.attentionOnly===true,hideEmpty:doc.hideEmpty===true,sparse:grid.includes(MATRIX_NONE),cellWidth:doc.cellWidth||32,density:doc.density||"normal",details:doc.details||"dialog",phone:doc.phone||"scroll",legend:doc.legend!==false,autoHeight:doc.autoHeight!==false,labels:doc.labels})}
|
|
31
|
+
const MATRIX_AXES=["rows","columns"];function matrixNatural(a,b){const x=foldText(a),y=foldText(b),digits=/(\d+)|(\D+)/g;
|
|
32
|
+
const left=x.match(digits)||[],right=y.match(digits)||[];for(let i=0;i<Math.min(left.length,right.length);i++){const p=left[i],q=right[i],numeric=/^\d/.test(p) && /^\d/.test(q);
|
|
33
|
+
if(numeric){const s=p.replace(/^0+/, ""), t = q.replace(/^0+/, ""); if (s.length !== t.length) return s.length - t.length; if (s !== t) return s < t ? -1 : 1; }
|
|
34
|
+
else if(p!==q)return p<q?-1:1}
|
|
35
|
+
return left.length-right.length||(a<b?-1:a>b?1:0)}
|
|
36
|
+
function matrixValueKey(value){return value==null||value.value==null?"":String(value.value)}
|
|
37
|
+
function matrixValueText(value,attribute,format){if(!value||value.value==null)return value&&value.text?value.text:"";if(value.text)return value.text;if(attribute.kind==="number")return format.format(value.value);if(attribute.kind==="datetime")return String(value.value).slice(0,16).replace("T"," ");return String(value.value)}
|
|
38
|
+
class MatrixModel{constructor(doc,format){this.doc=doc;this.format=format;this.cache=new Map()}
|
|
39
|
+
initialQuery(){const doc=this.doc,axis=name=>doc[name];return{search:"",attentionOnly:doc.attentionOnly,hideEmpty:doc.hideEmpty,filters:{rows:Object.create(null),columns:Object.create(null)},sort:{rows:axis("rows").sort,columns:axis("columns").sort},group:{rows:axis("rows").groupBy,columns:axis("columns").groupBy},collapsed:{rows:[],columns:[]}}}
|
|
40
|
+
itemText(item,axis){return[item.label,item.secondary||"",...axis.attributes.map(attribute=>matrixValueText(item.values[attribute.id],attribute,this.format))].join(" ")}
|
|
41
|
+
candidates(axis,filters,search){const entries=Object.entries(filters||{}).filter(([,values])=>values&&values.length);const passes=item=>entries.every(([id,values])=>values.includes(matrixValueKey(item.values[id])));const found=axis.items.filter(passes);if(!search)return{items:found,searched:false};const term=foldText(search),hits=found.filter(item=>foldText(this.itemText(item,axis)).includes(term));return{items:hits,searched:true,hits:hits.length}}
|
|
42
|
+
derive(query){const doc=this.doc,C=doc.columns.items.length,grid=doc.grid;this.cache.clear();this.query=query;const search=query.search.trim();let rows=this.candidates(doc.rows,query.filters.rows,search),columns=this.candidates(doc.columns,query.filters.columns,doc.square?"":search);if(!doc.square&&search){if(!rows.hits&&columns.hits)rows=this.candidates(doc.rows,query.filters.rows,"");else if(rows.hits&&!columns.hits)columns=this.candidates(doc.columns,query.filters.columns,"")}
|
|
43
|
+
let rowItems=rows.items,columnItems=columns.items;if(query.attentionOnly||query.hideEmpty){const attention=doc.attention,only=query.attentionOnly,keepRow=new Uint8Array(doc.rows.items.length),keepColumn=new Uint8Array(C);for(const row of rowItems)for(const column of columnItems){const state=grid[row.index*C+column.index];if(state<MATRIX_SELF&&(!only||attention[state])){keepRow[row.index]=1;keepColumn[column.index]=1}}
|
|
44
|
+
rowItems=rowItems.filter(item=>keepRow[item.index]);columnItems=columnItems.filter(item=>keepColumn[item.index])}
|
|
45
|
+
const rowTotal=new Float64Array(doc.rows.items.length),columnTotal=new Float64Array(C),counts=new Float64Array(doc.states.length+1),counted=doc.total;for(const row of rowItems)for(const column of columnItems){const state=grid[row.index*C+column.index];if(state===MATRIX_SELF)continue;if(state===MATRIX_NONE){counts[doc.states.length]++;continue}
|
|
46
|
+
counts[state]++;if(counted[state]){rowTotal[row.index]++;columnTotal[column.index]++}}
|
|
47
|
+
this.rowTotal=rowTotal;this.columnTotal=columnTotal;this.counts=counts;this.visible={rows:rowItems.length,columns:columnItems.length};this.rows=this.arrange("rows",rowItems,rowTotal,query);this.columns=this.arrange("columns",columnItems,columnTotal,query);let grand=0;for(const row of rowItems)grand +=rowTotal[row.index];this.grandTotal=grand;return this}
|
|
48
|
+
compare(axis,sort,totals){if(!sort)return(a,b)=>a.index-b.index;const sign=sort.direction==="desc"?-1:1;if(sort.by==="#label")return(a,b)=>sign*matrixNatural(a.label,b.label)||a.index-b.index;if(sort.by==="#total")return(a,b)=>sign*(totals[a.index]- totals[b.index])||matrixNatural(a.label,b.label)||a.index-b.index;const attribute=axis.attributeById.get(sort.by);return(a,b)=>{const x=a.values[attribute.id]?.value??null,y=b.values[attribute.id]?.value??null;if(x===null||y===null)return x===y?a.index-b.index:x===null?1:-1;const result=attribute.kind==="number"?x-y:attribute.kind==="datetime"?matrixMoment(x)- matrixMoment(y):matrixNatural(String(x),String(y));return sign*result||a.index-b.index}}
|
|
49
|
+
arrange(name,items,totals,query){const axis=this.doc[name],compare=this.compare(axis,query.sort[name],totals),sorted=items.slice().sort(compare);const entries=[],group=query.group[name];if(!group){for(const item of sorted)entries.push({kind:"item",key:"i"+item.index,item,label:item.label,members:[item],total:totals[item.index]});return entries}
|
|
50
|
+
const attribute=axis.attributeById.get(group),groups=new Map();for(const item of sorted){const key=matrixValueKey(item.values[group]);if(!groups.has(key))groups.set(key,{key,label:matrixValueText(item.values[group],attribute,this.format)||"—",members:[],total:0});const entry=groups.get(key);entry.members.push(item);entry.total +=totals[item.index]}
|
|
51
|
+
const sort=query.sort[name],sign=sort&&sort.direction==="desc"?-1:1;const ordered=[...groups.values()].sort((a,b)=>sort&&sort.by==="#total"?sign*(a.total-b.total)||matrixNatural(a.label,b.label):sign*matrixNatural(a.label,b.label));const collapsed=new Set(query.collapsed[name]);for(const entry of ordered){const closed=collapsed.has(entry.key),base={group:entry,key:"g"+entry.key,label:entry.label,members:entry.members,total:entry.total};if(closed)entries.push(Object.assign({kind:"rollup"},base));else{if(name==="rows")entries.push(Object.assign({kind:"heading"},base));for(const item of entry.members)entries.push({kind:"item",key:"i"+item.index,item,label:item.label,members:[item],total:totals[item.index],band:entry})}}
|
|
52
|
+
return entries}
|
|
53
|
+
cell(row,column){const doc=this.doc,C=doc.columns.items.length;if(row.kind==="item"&&column.kind==="item"){const at=row.item.index*C+column.item.index,state=doc.grid[at];return{state,attention:state<MATRIX_SELF&&doc.attention[state]?1:0,note:doc.notes.get(at)||null,members:1}}
|
|
54
|
+
const key=row.key+"\u0000"+column.key,cached=this.cache.get(key);if(cached)return cached;let worst=MATRIX_NONE,attention=0,members=0;for(const r of row.members)for(const c of column.members){const state=doc.grid[r.index*C+c.index];if(state>=MATRIX_SELF)continue;members++;if(state<worst)worst=state;if(doc.attention[state])attention++}
|
|
55
|
+
const result={state:worst,attention,note:null,members,rolled:true};if(this.cache.size>20000)this.cache.clear();this.cache.set(key,result);return result}}
|
|
56
|
+
const MATRIX_SYMBOL_PATHS={check:"M3.5 8.4l2.9 2.9 6.1-6.6",cross:"M4.5 4.5l7 7M11.5 4.5l-7 7",warning:"M8 2.6l5.8 10.4H2.2zM8 6.6v3M8 11.4v.2",info:"M8 1.9a6.1 6.1 0 1 0 0 12.2A6.1 6.1 0 0 0 8 1.9zM8 7.3v3.9M8 4.9v.2",dot:"",dash:"M4.2 8h7.6",question:"M5.9 6.1a2.1 2.1 0 1 1 2.8 2c-.5.2-.7.6-.7 1.1v.5M8 11.6v.2",clock:"M8 1.9a6.1 6.1 0 1 0 0 12.2A6.1 6.1 0 0 0 8 1.9zM8 4.8V8l2.2 1.4",pause:"M6 4.4v7.2M10 4.4v7.2"};const MATRIX_SYMBOL_CACHE=new Map();function matrixSymbol(name){if(!name)return null;let template=MATRIX_SYMBOL_CACHE.get(name);if(!template){const ns="http://www.w3.org/2000/svg";template=document.createElementNS(ns,"svg");template.setAttribute("viewBox","0 0 16 16");template.setAttribute("aria-hidden","true");template.setAttribute("focusable","false");template.setAttribute("class","cx-mx-symbol");if(name==="dot"){const circle=document.createElementNS(ns,"circle");circle.setAttribute("cx","8");circle.setAttribute("cy","8");circle.setAttribute("r","3.6");circle.setAttribute("fill","currentColor");template.appendChild(circle)}else{const path=document.createElementNS(ns,"path");path.setAttribute("d",MATRIX_SYMBOL_PATHS[name]);path.setAttribute("fill","none");path.setAttribute("stroke","currentColor");path.setAttribute("stroke-width","1.8");path.setAttribute("stroke-linecap","round");path.setAttribute("stroke-linejoin","round");template.appendChild(path)}
|
|
57
|
+
MATRIX_SYMBOL_CACHE.set(name,template)}
|
|
58
|
+
return template.cloneNode(true)}
|
|
59
|
+
const MATRIX_ROW_HEIGHTS={compact:26,normal:30,comfortable:36},MATRIX_TOTAL_WIDTH=64,MATRIX_OVERSCAN=96;class MatrixView{constructor(host,options){this.host=host;this.options=options;this.lifetime=new AbortController();this.doc=matrixDocument(options.view);this.labels=matrixLabels(this.doc.labels,options.labels);this.lang=host.closest("[lang]")?.getAttribute("lang")||document.documentElement.lang||"en";this.format=numberFormat(this.lang);this.model=new MatrixModel(this.doc,this.format);this.query=this.model.initialQuery();if(options.state)this.applyState(options.state,false);this.rowWindow=new AxisWindow();this.columnWindow=new AxisWindow();this.rowsMounted=new Map();this.rowPool=[];this.tilePool=[];this.headMounted=new Map();this.footMounted=new Map();this.bandMounted=new Map();this.focus={row:0,column:0};this.build();this.derive()}
|
|
60
|
+
emit(name,detail,callback,cancelable){const event=new CustomEvent("canopyx:"+name,{detail,bubbles:true,cancelable:!!cancelable});const proceed=this.host.dispatchEvent(event);if(typeof this.options[callback]==="function"){try{this.options[callback](detail,event)}catch(error){this.reportError(error,callback)}}
|
|
61
|
+
return proceed&&!event.defaultPrevented}
|
|
62
|
+
reportError(error,hook){this.host.dispatchEvent(new CustomEvent("canopyx:error",{detail:{error,hook},bubbles:true}))}
|
|
63
|
+
build(){const host=this.host,doc=this.doc,signal=this.lifetime.signal;if(!host.hasAttribute("data-cx-theme")){host.setAttribute("data-cx-theme","auto");this.addedTheme=true}
|
|
64
|
+
const root=this.root=h("div","cx-matrix cx-mx-"+doc.density);this.bar=h("div","cx-mx-bar");this.legend=h("div","cx-mx-legend");this.legend.setAttribute("role","list");this.legend.setAttribute("aria-label",this.labels.matrixLegend);this.legend.hidden=!doc.legend;const scroll=this.scroll=h("div","cx-mx-scroll");scroll.setAttribute("role","grid");scroll.setAttribute("aria-label",doc.label);this.head=h("div","cx-mx-head");this.head.setAttribute("role","rowgroup");this.band=h("div","cx-mx-band");this.band.setAttribute("role","row");this.headRow=h("div","cx-mx-hrow");this.headRow.setAttribute("role","row");this.corner=h("div","cx-mx-corner");this.corner.setAttribute("role","columnheader");this.corner.setAttribute("aria-colindex","1");this.headSpacer=h("div","cx-mx-spacer");this.headTotal=h("div","cx-mx-total cx-mx-htotal");this.headTotal.setAttribute("role","columnheader");this.band.setAttribute("aria-rowindex","1");this.headRow.append(this.corner,this.headSpacer,this.headTotal);this.bandCorner=h("div","cx-mx-corner cx-mx-bandcorner");this.bandSpacer=h("div","cx-mx-spacer");this.bandEnd=h("div","cx-mx-total cx-mx-bandend");this.band.append(this.bandCorner,this.bandSpacer,this.bandEnd);this.head.append(this.band,this.headRow);this.body=h("div","cx-mx-body");this.body.setAttribute("role","rowgroup");this.foot=h("div","cx-mx-foot");this.foot.setAttribute("role","rowgroup");this.footRow=h("div","cx-mx-frow");this.footRow.setAttribute("role","row");this.footHeader=h("div","cx-mx-rowhead cx-mx-foothead");this.footHeader.setAttribute("role","rowheader");this.footHeader.setAttribute("aria-colindex","1");this.footSpacer=h("div","cx-mx-spacer");this.footGrand=h("div","cx-mx-total cx-mx-grand");this.footGrand.setAttribute("role","gridcell");this.footRow.append(this.footHeader,this.footSpacer,this.footGrand);this.foot.appendChild(this.footRow);this.empty=h("div","cx-mx-empty");this.empty.hidden=true;scroll.append(this.head,this.body,this.foot);this.live=h("div","cx-sr-only");this.live.setAttribute("aria-live","polite");this.tip=h("div","cx-mx-tip");this.tip.setAttribute("aria-hidden","true");this.tip.hidden=true;this.cards=h("div","cx-mx-cards");this.cards.hidden=true;this.cards.tabIndex=0;this.cards.setAttribute("role","region");this.cards.setAttribute("aria-label",doc.label);this.print=h("div","cx-mx-print");root.append(this.bar,this.legend,scroll,this.empty,this.cards,this.live,this.tip,this.print);host.appendChild(root);this.buildBar();scroll.addEventListener("scroll",()=>{this.hideTip();this.scheduleRender()},{passive:true,signal});scroll.addEventListener("keydown",event=>this.onKey(event),{signal});scroll.addEventListener("click",event=>this.onClick(event),{signal});scroll.addEventListener("focusin",event=>this.onFocus(event),{signal});scroll.addEventListener("pointermove",event=>this.onPointer(event),{signal});scroll.addEventListener("pointerleave",()=>this.hideTip(),{signal});global.addEventListener("beforeprint",()=>this.renderPrint(),{signal});this.resize=new ResizeObserver(()=>{if(this.destroyed)return;const focused=this.scroll.contains(document.activeElement);this.layout();this.render();if(focused&&!this.scroll.contains(document.activeElement)){if(!this.scroll.hidden)this.moveTo(this.focus.row,this.focus.column,true,false);else if(!this.cards.hidden)this.cards.focus({preventScroll:true})}});this.refreshChrome();this.resize.observe(host);const restyle=()=>{if(!this.destroyed){this.lookCache=null;this.scheduleRender()}};this.themeWatch=new MutationObserver(restyle);this.themeWatch.observe(host,{attributes:true,attributeFilter:["data-cx-theme","class","style"]});for(const query of["(prefers-color-scheme: dark)","(forced-colors: active)"])global.matchMedia?.(query).addEventListener?.("change",restyle,{signal})}
|
|
65
|
+
refreshChrome(){const doc=this.doc,labels=this.labels;this.scroll.setAttribute("aria-label",doc.label);this.cards.setAttribute("aria-label",doc.label);this.legend.setAttribute("aria-label",labels.matrixLegend);this.legend.hidden=!doc.legend;setText(this.headTotal,labels.matrixTotal);setText(this.footHeader,labels.matrixTotal);setText(this.empty,labels.matrixNoMatches);const columnsTitle=h("span","cx-mx-corner-columns"),rowsTitle=h("span","cx-mx-corner-rows");setText(columnsTitle,doc.columnTitle||labels.matrixColumns);setText(rowsTitle,doc.rows.title||labels.matrixRows);this.corner.replaceChildren(columnsTitle,rowsTitle);this.refreshBar?.()}
|
|
66
|
+
derive(announce,anchor=this.captureFocus()){const focused=this.scroll.contains(document.activeElement);this.model.derive(this.query);this.restoreFocus(anchor);this.syncBar();this.paintLegend();this.layout();this.recycleAll();this.render();if(focused&&!this.scroll.hidden)this.moveTo(this.focus.row,this.focus.column,true,false);if(announce)setText(this.live,this.summaryText())}
|
|
67
|
+
layout(){const doc=this.doc,width=this.host.clientWidth||800,phone=width<=640;this.phone=phone;this.root.classList.toggle("cx-mx-phone",phone);this.rtl=getComputedStyle(this.root).direction==="rtl";this.rowH=MATRIX_ROW_HEIGHTS[doc.density]+(phone?4:0);this.cellW=doc.cellWidth;const measure=this.measurer||=document.createElement("canvas").getContext("2d");const style=getComputedStyle(this.root),font=style.fontWeight+" "+style.fontSize+" "+style.fontFamily;const widest=(items,extra=0)=>{let max=0;if(measure){measure.font=font;for(const item of items)max=Math.max(max,measure.measureText(item.label).width+extra)}return max};const grouped=!!this.query.group.rows;const secondary=!phone&&this.model.rows.some(entry=>entry.kind==="item"&&entry.item.secondary)?Math.min(140,widest(this.model.rows.filter(entry=>entry.kind==="item"&&entry.item.secondary).map(entry=>({label:entry.item.secondary})))*0.88+6):0;const rowNeed=Math.ceil(widest(this.model.rows)+ secondary+28 +(grouped?14:0));this.rowHeadW=Math.min(phone?Math.max(96,Math.floor(width*0.42)):280,Math.max(phone?96:120,rowNeed));this.headH=Math.min(160,Math.max(56,Math.ceil(widest(this.model.columns)+ 20)));this.bandH=this.query.group.columns?28:0;this.totalW=doc.rowTotals?phone?48:MATRIX_TOTAL_WIDTH:0;this.footH=doc.columnTotals?this.rowH:0;const geometry=[this.rowH,this.rowHeadW,this.cellW,this.headH,this.bandH,this.totalW,this.rtl,phone].join(":");if(this.geometry!==undefined&&this.geometry!==geometry)this.recycleAll();this.geometry=geometry;this.rowWindow.reset(this.rowH,this.model.rows.length);this.columnWindow.reset(this.cellW,this.model.columns.length);const contentW=this.rowHeadW+this.columnWindow.total+this.totalW;const root=this.root.style;root.setProperty("--mx-row-h",this.rowH+"px");root.setProperty("--mx-cell-w",this.cellW+"px");root.setProperty("--mx-head-w",this.rowHeadW+"px");root.setProperty("--mx-head-h",this.headH+"px");root.setProperty("--mx-band-h",this.bandH+"px");root.setProperty("--mx-total-w",this.totalW+"px");root.setProperty("--mx-content-w",contentW+"px");root.setProperty("--mx-cols-w",this.columnWindow.total+"px");this.body.style.height=this.rowWindow.total+"px";this.band.hidden=!this.bandH;this.foot.hidden=!this.footH;this.headTotal.hidden=this.bandEnd.hidden=!this.totalW;this.footGrand.hidden=!this.totalW;const nothing=!this.model.rows.length||!this.model.columns.length;this.empty.hidden=!nothing;this.scroll.hidden=nothing;const cards=phone&&doc.phone==="cards";this.cards.hidden=!cards||nothing;if(cards)this.scroll.hidden=true;this.scroll.setAttribute("aria-rowcount",String(this.model.rows.length +(this.bandH?2:1)+(this.footH?1:0)));this.headRow.setAttribute("aria-rowindex",this.bandH?"2":"1");this.headTotal.setAttribute("aria-colindex",String(this.model.columns.length+2));this.scroll.setAttribute("aria-colcount",String(this.model.columns.length+1 +(this.totalW?1:0)));if(cards)this.renderCards();this.fitHeight()}
|
|
68
|
+
fitHeight(){if(this.fitFrame)return;this.fitFrame=requestAnimationFrame(()=>{this.fitFrame=0;if(!this.destroyed)this.fitNow()})}
|
|
69
|
+
fitNow(){const host=this.host,style=host.style;if(this.fitInline===undefined){const own=getComputedStyle(host).maxHeight;this.fitInline=style.maxHeight;this.fitMax=own==="none"?Infinity:/px$/.test(own) ? parseFloat(own) : NaN;
|
|
70
|
+
}
|
|
71
|
+
if(this.doc.autoHeight===false||Number.isNaN(this.fitMax)){if(style.maxHeight!==this.fitInline)style.maxHeight=this.fitInline;return}
|
|
72
|
+
if(!host.offsetHeight)return;const parts=[this.bar,this.legend,this.scroll,this.empty,this.cards].filter(part=>!part.hidden),hostStyle=getComputedStyle(host);let content=(parts.length-1)*(parseFloat(getComputedStyle(this.root).rowGap)||0);for(const part of parts){let height=part.getBoundingClientRect().height;if(part===this.scroll||part===this.cards)height=Math.min(height+part.scrollHeight-part.clientHeight,parseFloat(getComputedStyle(part).maxHeight)||Infinity);content +=height}
|
|
73
|
+
const chrome=["paddingTop","paddingBottom","borderTopWidth","borderBottomWidth"].reduce((sum,name)=>sum +(parseFloat(hostStyle[name])||0),0);const value=Math.min(this.fitMax,Math.ceil(chrome+content))+ "px";if(style.maxHeight!==value)style.maxHeight=value}}
|
|
74
|
+
Object.assign(MatrixView.prototype,{scheduleRender(){if(this.destroyed||this.frame)return;this.frame=requestAnimationFrame(()=>{this.frame=0;this.render()})},recycleAll(){this.lookCache=null;for(const row of this.rowsMounted.values())this.releaseRow(row);this.rowsMounted.clear();for(const map of[this.headMounted,this.footMounted,this.bandMounted]){for(const element of map.values())element.remove();map.clear()}},releaseRow(row){row.remove();row._key=null;if(this.rowPool.length<200)this.rowPool.push(row)},windows(){const scroll=this.scroll,viewH=Math.max(this.rowH,scroll.clientHeight-this.headH-this.bandH-this.footH),viewW=Math.max(this.cellW,scroll.clientWidth-this.rowHeadW-this.totalW);const rows=this.rowWindow.window(scroll.scrollTop,viewH,MATRIX_OVERSCAN),columns=this.columnWindow.window(Math.abs(scroll.scrollLeft),viewW,MATRIX_OVERSCAN);return{rows,columns,viewH,viewW}},render(){if(this.destroyed||this.scroll.hidden)return;const{rows,columns}=this.windows(),model=this.model,focus=this.focus,scroll=this.scroll,top=scroll.scrollTop;const below=scroll.scrollHeight-scroll.clientHeight-top>1;this.head.classList.toggle("cx-mx-raised",top>0);this.foot.classList.toggle("cx-mx-raised",below);const columnList=[];for(let c=columns.first;c<=columns.last;c++)columnList.push(c);if(focus.column>=0&&focus.column<model.columns.length&&(focus.column<columns.first||focus.column>columns.last))columnList.push(focus.column);const rowList=[];for(let r=rows.first;r<=rows.last;r++)rowList.push(r);if(focus.row>=0&&focus.row<model.rows.length&&(focus.row<rows.first||focus.row>rows.last))rowList.push(focus.row);this.paintHeader(columnList);this.paintBand(columns);this.placeBands();this.paintFoot(columnList);const keep=new Set();for(const r of rowList){const entry=model.rows[r];if(!entry)continue;keep.add(entry.key);let row=this.rowsMounted.get(entry.key);if(!row){row=this.rowPool.pop()||this.newRow();this.rowsMounted.set(entry.key,row);this.fillRow(row,entry,r);this.body.appendChild(row)}
|
|
75
|
+
else if(row._index!==r)this.placeRow(row,r);this.drawRow(row,entry,columns.first,entry.kind==="heading"?-1:columns.last)}
|
|
76
|
+
for(const[key,row]of this.rowsMounted)if(!keep.has(key)){this.releaseRow(row);this.rowsMounted.delete(key)}
|
|
77
|
+
this.syncTabStop()},newRow(){const row=h("div","cx-mx-row"),head=h("div","cx-mx-rowhead"),spacer=h("div","cx-mx-spacer"),total=h("div","cx-mx-total");row.setAttribute("role","row");head.setAttribute("role","rowheader");head.setAttribute("aria-colindex","1");total.setAttribute("role","gridcell");row.append(head,spacer,total);row._head=head;row._total=total;return row},placeRow(row,r){row._index=r;row.style.transform="translateY("+this.rowWindow.offset(r)+ "px)";row.setAttribute("aria-rowindex",String(r +(this.bandH?3:2)));row._head._r=r;row._total._r=r},fillRow(row,entry,r){row._key=entry.key;row._entry=entry;this.placeRow(row,r);row.className="cx-mx-row" +(entry.kind==="heading"?" cx-mx-heading":entry.kind==="rollup"?" cx-mx-rolledrow":entry.band?" cx-mx-member":"");const head=row._head,labels=this.labels;head.replaceChildren();head._c=-1;head.tabIndex=-1;if(entry.kind==="item"){const item=entry.item,text=item.href?h("a","cx-mx-label cx-link"):h("span","cx-mx-label");if(item.href){text.href=item.href;text.tabIndex=-1}
|
|
78
|
+
setText(text,item.label);head.appendChild(text);if(item.secondary){const sub=h("span","cx-mx-sub");setText(sub,item.secondary);head.appendChild(sub)}
|
|
79
|
+
head.title=item.secondary?item.label+" — "+item.secondary:item.label}else{const toggle=h("button","cx-mx-toggle"),open=entry.kind==="heading";toggle.type="button";toggle.tabIndex=-1;toggle.setAttribute("aria-expanded",String(open));toggle.setAttribute("aria-label",fill(open?labels.matrixCollapseGroup:labels.matrixExpandGroup,{group:entry.label}));toggle._group={axis:"rows",key:entry.group.key};const label=h("span","cx-mx-label"),count=h("span","cx-mx-sub");setText(label,entry.label);setText(count,fill(labels.matrixGroupCount,{count:this.format.format(entry.members.length)}));toggle.append(label,count);head.appendChild(toggle);head.removeAttribute("title")}
|
|
80
|
+
const total=row._total;total.hidden=!this.totalW;if(this.totalW)this.paintTotal(total,entry,r,this.model.columns.length)},paintTotal(element,entry,r,c){const value=entry.total||0;setText(element,this.format.format(value));element.classList.toggle("cx-mx-zero",!value);element.setAttribute("aria-label",fill(this.labels.matrixItemTotal,{item:entry.label,count:this.format.format(value)}));element._r=r;element._c=c;element.tabIndex=-1;element.setAttribute("aria-colindex",String(this.model.columns.length+2))},paintHeader(columnList){const wanted=new Set(columnList),mounted=this.headMounted,model=this.model;for(const[c,element]of mounted)if(!wanted.has(c)||element._key!==model.columns[c]?.key){element.remove();mounted.delete(c)}
|
|
81
|
+
for(const c of columnList){const entry=model.columns[c];if(!entry||mounted.has(c))continue;const element=h("div","cx-mx-hcell" +(entry.kind==="rollup"?" cx-mx-rolledcol":""));element.setAttribute("role","columnheader");element.setAttribute("aria-colindex",String(c+2));element.style.insetInlineStart=this.rowHeadW+this.columnWindow.offset(c)+ "px";element._r=-1;element._c=c;element._key=entry.key;element.tabIndex=-1;if(entry.kind==="rollup"){const toggle=h("button","cx-mx-toggle");toggle.type="button";toggle.tabIndex=-1;toggle.setAttribute("aria-expanded","false");toggle.setAttribute("aria-label",fill(this.labels.matrixExpandGroup,{group:entry.label}));toggle._group={axis:"columns",key:entry.group.key};const text=h("span","cx-mx-vlabel");setText(text,entry.label);toggle.appendChild(text);element.appendChild(toggle)}else{const item=entry.item,text=h(item.href?"a":"span","cx-mx-vlabel" +(item.href?" cx-link":""));if(item.href){text.href=item.href;text.tabIndex=-1}
|
|
82
|
+
setText(text,item.label);element.appendChild(text);element.title=item.secondary?item.label+" — "+item.secondary:item.label}
|
|
83
|
+
mounted.set(c,element);this.headRow.appendChild(element)}},paintBand(columns){const mounted=this.bandMounted,model=this.model;if(!this.bandH)return;const seen=new Set();for(let c=columns.first;c<=columns.last&&c>=0;c++){const entry=model.columns[c];if(!entry||!entry.band||seen.has(entry.band.key))continue;seen.add(entry.band.key);if(mounted.has(entry.band.key))continue;const band=entry.band,first=model.columns.findIndex(item=>item.band===band),element=h("div","cx-mx-bcell");element._from=this.columnWindow.offset(first);element._width=band.members.length*this.cellW;element.style.insetInlineStart=this.rowHeadW+element._from+"px";element.style.width=element._width+"px";element.setAttribute("role","columnheader");element.setAttribute("aria-colindex",String(first+2));element.setAttribute("aria-colspan",String(band.members.length));const toggle=h("button","cx-mx-toggle");toggle.type="button";toggle.tabIndex=-1;toggle.setAttribute("aria-expanded","true");toggle.setAttribute("aria-label",fill(this.labels.matrixCollapseGroup,{group:band.label}));toggle._group={axis:"columns",key:band.key};const text=h("span","cx-mx-label");setText(text,band.label);toggle.appendChild(text);element.appendChild(toggle);element.title=band.label;mounted.set(band.key,element);this.band.appendChild(element)}
|
|
84
|
+
for(const[key,element]of mounted)if(!seen.has(key)){element.remove();mounted.delete(key)}},placeBands(){if(!this.bandH)return;const start=Math.abs(this.scroll.scrollLeft),end=start+Math.max(0,this.scroll.clientWidth-this.rowHeadW-this.totalW);for(const element of this.bandMounted.values()){const from=element._from,shown=Math.min(end,from+element._width)- Math.max(start,from),toggle=element.firstChild;toggle.style.marginInlineStart=Math.max(0,start-from)+ "px";toggle.style.maxWidth=Math.max(0,shown-8)+ "px";element.classList.toggle("cx-mx-bclip",shown<Math.min(40,element._width))}},paintFoot(columnList){if(!this.footH)return;const wanted=new Set(columnList),mounted=this.footMounted,model=this.model,rows=model.rows.length +(this.bandH?3:2);this.footRow.setAttribute("aria-rowindex",String(rows));for(const[c,element]of mounted)if(!wanted.has(c)||element._key!==model.columns[c]?.key){element.remove();mounted.delete(c)}
|
|
85
|
+
for(const c of columnList){const entry=model.columns[c];if(!entry||mounted.has(c))continue;const element=h("div","cx-mx-fcell");element.setAttribute("role","gridcell");element.style.insetInlineStart=this.rowHeadW+this.columnWindow.offset(c)+ "px";this.paintTotal(element,entry,model.rows.length,c);element.setAttribute("aria-colindex",String(c+2));element._key=entry.key;mounted.set(c,element);this.footRow.appendChild(element)}
|
|
86
|
+
if(this.totalW)this.paintTotal(this.footGrand,{label:this.labels.matrixTotal,total:model.grandTotal},model.rows.length,model.columns.length);this.footHeader._r=model.rows.length;this.footHeader._c=-1}});const MATRIX_PATHS=new Map(),MATRIX_TILE=16;function matrixPath(name){if(!MATRIX_PATHS.has(name))MATRIX_PATHS.set(name,name==="dot"?null:new Path2D(MATRIX_SYMBOL_PATHS[name]));return MATRIX_PATHS.get(name)}
|
|
87
|
+
Object.assign(MatrixView.prototype,{looks(){if(this.lookCache&&this.lookCache.doc===this.doc)return this.lookCache.items;const items=new Map(),probe=h("span","cx-mx-cell"),forced=global.matchMedia?.("(forced-colors: active)").matches;probe.style.display="none";this.root.appendChild(probe);const read=(cls,tone)=>{probe.className=cls;toneColors(probe,tone);const style=getComputedStyle(probe),fill=style.backgroundColor;return{ink:style.color,fill:/rgba\(\d+, \d+, \d+, 0\)|transparent/.test(fill) ? null : fill, font: style.fontWeight + " " + style.fontSize + " " + style.fontFamily };
|
|
88
|
+
};this.doc.states.forEach((state,index)=>{const tone=toneOf(state.tone),look=read("cx-mx-cell" +(state.attention?" cx-mx-attn":"")+ tone.cls,tone);items.set(index,Object.assign(look,{symbol:state.symbol,name:state.label,attention:state.attention,forced}))});items.set(MATRIX_NONE,Object.assign(read("cx-mx-cell cx-mx-none",toneOf(null)),{symbol:null,name:this.labels.matrixNoData,none:true}));items.set(MATRIX_SELF,Object.assign(read("cx-mx-cell cx-mx-self",toneOf(null)),{symbol:null,name:this.labels.matrixSelf,self:true}));probe.className="cx-mx-gridline";this.lineColor=getComputedStyle(probe).color;probe.remove();this.lookCache={doc:this.doc,items};this.cellName=matrixTemplate(this.labels.matrixCell);this.canvasVersion=(this.canvasVersion||0)+ 1;return items},drawRow(row,entry,first,last){const ratio=global.devicePixelRatio||1,tiles=row._tiles||=new Map(),version=this.canvasVersion+":"+ratio+":"+entry.key;this.looks();const from=last>=first?Math.floor(first / MATRIX_TILE) : 0, to = last >= first ? Math.floor(last / MATRIX_TILE) : -1;
|
|
89
|
+
for(const[index,tile]of tiles)if(index<from||index>to){tiles.delete(index);tile.remove();if(this.tilePool.length<400)this.tilePool.push(tile)}
|
|
90
|
+
for(let index=from;index<=to;index++){let tile=tiles.get(index);if(!tile){tile=this.tilePool.pop();if(!tile){tile=h("canvas","cx-mx-canvas");tile.setAttribute("aria-hidden","true")}
|
|
91
|
+
tile._drawn=null;tiles.set(index,tile);row.insertBefore(tile,row._total)}
|
|
92
|
+
if(tile._drawn!==version){this.drawTile(tile,entry,index,ratio);tile._drawn=version}}},drawTile(canvas,entry,index,ratio){const looks=this.lookCache.items,cellW=this.cellW,height=this.rowH-1,columns=this.model.columns;const first=index*MATRIX_TILE,last=Math.min(columns.length-1,first+MATRIX_TILE-1),width=(last-first+1)*cellW;const pixelsW=Math.ceil(width*ratio),pixelsH=Math.ceil(height*ratio);if(canvas.width!==pixelsW||canvas.height!==pixelsH){canvas.width=pixelsW;canvas.height=pixelsH;canvas.style.width=width+"px";canvas.style.height=height+"px"}
|
|
93
|
+
canvas.style.insetInlineStart=this.rowHeadW+this.columnWindow.offset(first)+ "px";const ctx=canvas.getContext("2d"),rtl=this.rtl;ctx.setTransform(ratio,0,0,ratio,0,0);ctx.clearRect(0,0,width,height);ctx.textBaseline="middle";for(let c=first;c<=last;c++){const info=this.model.cell(entry,columns[c]),look=looks.get(info.state),x=rtl?width -(c-first+1)*cellW:(c-first)*cellW;if(look.self){ctx.save();ctx.beginPath();ctx.rect(x,0,cellW,height);ctx.clip();ctx.beginPath();for(let d=-height;d<cellW;d +=5){ctx.moveTo(x+d,height);ctx.lineTo(x+d+height,0)}
|
|
94
|
+
ctx.strokeStyle=this.lineColor;ctx.lineWidth=1;ctx.stroke();ctx.restore()}else if(look.none){ctx.fillStyle=look.ink;ctx.beginPath();ctx.arc(x+cellW / 2, height / 2, 1.25, 0, Math.PI * 2); ctx.fill();
|
|
95
|
+
}else if(look.fill){ctx.fillStyle=look.fill;ctx.fillRect(x,0,cellW,height);if(look.forced){ctx.strokeStyle=look.ink;ctx.lineWidth=1;ctx.strokeRect(x+1.5,1.5,cellW-3,height-3)}}
|
|
96
|
+
if(info.rolled){ctx.strokeStyle=this.lineColor;ctx.lineWidth=1;ctx.strokeRect(x+0.5,0.5,cellW-2,height-1)}
|
|
97
|
+
const note=info.note,shown=note&¬e.text!=null?note.text:note&¬e.value!=null?this.format.format(note.value):info.rolled&&info.attention?this.format.format(info.attention):"";let textWidth=0;if(shown){ctx.font=look.font;const cacheKey=look.font+"\u0000"+shown,widths=this.textWidths||=new Map();if(!widths.has(cacheKey)){if(widths.size>4000)widths.clear();widths.set(cacheKey,ctx.measureText(shown).width)}
|
|
98
|
+
textWidth=widths.get(cacheKey);if(look.symbol&&textWidth+18>cellW-4)textWidth=-1}
|
|
99
|
+
const symbolX=look.symbol?(textWidth>0?x+Math.max(2,(cellW-textWidth-16)/ 2) : x + (cellW - 14) / 2) : 0;
|
|
100
|
+
if(look.symbol)ctx.drawImage(this.sprite(look,ratio),Math.round(symbolX),Math.round((height-14)/ 2), 14, 14);
|
|
101
|
+
if(textWidth>0){ctx.fillStyle=look.ink;ctx.fillText(shown,look.symbol?symbolX+16:x +(cellW-textWidth)/ 2, height / 2 + 0.5); }
|
|
102
|
+
ctx.fillStyle=this.lineColor;ctx.fillRect(rtl?x:x+cellW-1,0,1,height)}},sprite(look,ratio){if(look.spriteRatio===ratio)return look.spriteCanvas;const canvas=look.spriteCanvas||(look.spriteCanvas=document.createElement("canvas")),size=Math.ceil(14*ratio);canvas.width=canvas.height=size;const ctx=canvas.getContext("2d");ctx.setTransform(size / 16, 0, 0, size / 16, 0, 0); ctx.clearRect(0, 0, 16, 16);
|
|
103
|
+
ctx.strokeStyle=ctx.fillStyle=look.ink;ctx.lineWidth=1.8;ctx.lineCap=ctx.lineJoin="round";const path=matrixPath(look.symbol);if(path)ctx.stroke(path);else{ctx.beginPath();ctx.arc(8,8,3.6,0,Math.PI*2);ctx.fill()}
|
|
104
|
+
look.spriteRatio=ratio;return canvas},placeFocusCell(r,c){const model=this.model,entry=model.rows[r],column=model.columns[c];const row=entry&&entry.kind!=="heading"&&column&&this.rowsMounted.get(entry.key);let cell=this.focusCellElement;if(!row){if(cell&&cell!==document.activeElement)cell.remove();return null}
|
|
105
|
+
if(!cell){cell=this.focusCellElement=h("div","cx-mx-cell cx-mx-focus");cell.setAttribute("role","gridcell");cell.tabIndex=-1}
|
|
106
|
+
const info=model.cell(entry,column),look=this.looks().get(info.state),note=info.note;const shown=note&¬e.text!=null?note.text:note&¬e.value!=null?this.format.format(note.value):info.rolled&&info.attention?this.format.format(info.attention):"";cell.setAttribute("aria-label",this.cellName({row:entry.label,column:column.label,state:look.name})+(shown?", "+shown:""));cell.setAttribute("aria-colindex",String(c+2));cell.style.insetInlineStart=this.rowHeadW+this.columnWindow.offset(c)+ "px";cell._r=r;cell._c=c;if(cell.parentNode!==row)row.appendChild(cell);return cell},cellAt(event){const row=event.target.closest&&event.target.closest(".cx-mx-row");if(!row||!row._entry||row._entry.kind==="heading"||event.target.closest(".cx-mx-rowhead,.cx-mx-total"))return null;const box=row.getBoundingClientRect(),from=this.rtl?box.right-event.clientX:event.clientX-box.left,x=from-this.rowHeadW;if(x<0||x>=this.columnWindow.total)return null;const c=this.columnWindow.at(x);return c<0||c>=this.model.columns.length?null:{_r:row._index,_c:c}},cellRect(r,c){const entry=this.model.rows[r],row=entry&&this.rowsMounted.get(entry.key);if(!row)return null;const box=row.getBoundingClientRect(),offset=this.rowHeadW+this.columnWindow.offset(c);const left=this.rtl?box.right-offset-this.cellW:box.left+offset;return{left,top:box.top,right:left+this.cellW,bottom:box.top+this.rowH,width:this.cellW,height:this.rowH}},hoverCell(target){const hover=this.hoverElement||=h("div","cx-mx-hover");const entry=target&&this.model.rows[target._r],row=entry&&this.rowsMounted.get(entry.key);if(!row){hover.remove();return}
|
|
107
|
+
hover.style.insetInlineStart=this.rowHeadW+this.columnWindow.offset(target._c)+ "px";if(hover.parentNode!==row)row.appendChild(hover)}});Object.assign(MatrixView.prototype,{captureFocus(){return MATRIX_AXES.map((axis,index)=>{const entries=this.model[axis]||[],at=index?this.focus.column:this.focus.row,entry=entries[at];return{at,end:entries.length>0&&at===entries.length,id:entry?.kind==="item"?entry.item.id:null,group:entry?.group?{attribute:this.model.query?.group[axis],key:entry.group.key}:null}})},restoreFocus(anchor){MATRIX_AXES.forEach((axis,index)=>{const entries=this.model[axis],saved=anchor[index];const found=saved.id!==null?entries.findIndex(entry=>entry.kind==="item"&&entry.item.id===saved.id):
|
|
108
|
+
saved.group&&saved.group.attribute===this.query.group[axis]?entries.findIndex(entry=>entry.group?.key===saved.group.key):-1;const last=entries.length -((index?this.doc.rowTotals:this.doc.columnTotals)?0:1);this.focus[index?"column":"row"]=Math.max(-1,Math.min(last,saved.end?entries.length:found>=0?found:Math.min(saved.at,entries.length-1)))})},focusElement(r,c){const model=this.model,last=model.columns.length;if(r===-1)return c===-1?this.corner:c===last?this.headTotal:this.headMounted.get(c)||null;if(r===model.rows.length)return c===-1?this.footHeader:c===last?this.footGrand:this.footMounted.get(c)||null;const entry=model.rows[r],row=entry&&this.rowsMounted.get(entry.key);if(!row)return null;if(c===-1||entry.kind==="heading")return row._head;return c===last?row._total:this.placeFocusCell(r,c)},syncTabStop(){const element=this.focusElement(this.focus.row,this.focus.column);if(this.focusCellElement&&element!==this.focusCellElement&&this.focusCellElement!==document.activeElement)this.focusCellElement.remove();if(this.tabStop&&this.tabStop!==element)this.tabStop.tabIndex=-1;if(element){element.tabIndex=0;this.tabStop=element}},bounds(){const model=this.model;return{top:-1,bottom:model.rows.length -(this.footH?0:1),left:-1,right:model.columns.length -(this.totalW?0:1)}},moveTo(r,c,focus=true,tip=true,reveal=true){const b=this.bounds();r=Math.max(b.top,Math.min(b.bottom,r));c=Math.max(b.left,Math.min(b.right,c));if(r===-1&&c===-1&&!focus)c=0;this.focus={row:r,column:c};if(reveal)this.ensureVisible(r,c);this.render();const element=this.focusElement(r,c);if(element&&focus){element.focus({preventScroll:true});if(tip&&element.classList.contains("cx-mx-cell"))this.showTip(element,true);else this.hideTip()}},ensureVisible(r,c){const scroll=this.scroll,rtl=this.rtl;if(r>=0&&r<this.model.rows.length){const top=this.rowWindow.offset(r),view=scroll.clientHeight-this.headH-this.bandH-this.footH;if(top<scroll.scrollTop)scroll.scrollTop=top;else if(top+this.rowH>scroll.scrollTop+view)scroll.scrollTop=top+this.rowH-view}
|
|
109
|
+
if(c>=0&&c<this.model.columns.length){const left=this.columnWindow.offset(c),view=scroll.clientWidth-this.rowHeadW-this.totalW;let at=Math.abs(scroll.scrollLeft);if(left<at)at=left;else if(left+this.cellW>at+view)at=left+this.cellW-view;scroll.scrollLeft=rtl?-at:at}},onKey(event){if(event.defaultPrevented||event.altKey||event.metaKey)return;const target=event.target;if(target.closest&&target.closest("input,select,textarea")||target.tagName==="BUTTON"&&target!==this.tabStop&&!target.closest(".cx-mx-rowhead,.cx-mx-hcell"))return;const{row,column}=this.focus,rtl=this.rtl,page=Math.max(1,Math.floor((this.scroll.clientHeight-this.headH-this.bandH-this.footH)/ this.rowH) - 1);
|
|
110
|
+
const b=this.bounds(),ctrl=event.ctrlKey;let next=null;switch(event.key){case "ArrowUp":next=[row-1,column];break;case "ArrowDown":next=[row+1,column];break;case "ArrowLeft":next=[row,column +(rtl?1:-1)];break;case "ArrowRight":next=[row,column +(rtl?-1:1)];break;case "Home":next=ctrl?[Math.max(0,b.top),b.left]:[row,b.left];break;case "End":next=ctrl?[b.bottom,b.right]:[row,b.right];break;case "PageUp":next=[row-page,column];break;case "PageDown":next=[row+page,column];break;case "Enter":case " ":this.activate(this.focusElement(row,column));event.preventDefault();return;case "Escape":if(!this.tip.hidden){this.hideTip();event.preventDefault()}return;default:return}
|
|
111
|
+
event.preventDefault();this.moveTo(next[0],next[1])},onFocus(event){const element=event.target.closest&&event.target.closest(".cx-mx-cell,.cx-mx-rowhead,.cx-mx-hcell,.cx-mx-total,.cx-mx-fcell,.cx-mx-corner");if(!element||element._r===undefined&&element!==this.corner)return;const r=element===this.corner?-1:element._r;const heading=this.model.rows[r]?.kind==="heading",c=element===this.corner?-1:heading?this.focus.column:element._c;if(r!==this.focus.row||c!==this.focus.column){this.focus={row:r,column:c};this.syncTabStop()}},onClick(event){const toggle=event.target.closest(".cx-mx-toggle");if(toggle&&toggle._group){this.toggleGroup(toggle._group.axis,toggle._group.key);return}
|
|
112
|
+
if(event.target.closest("a"))return;const target=this.cellAt(event);if(target){this.focus={row:target._r,column:target._c};this.render();const cell=this.focusElement(target._r,target._c);cell?.focus({preventScroll:true});this.openCell(cell||target)}},activate(element){if(!element)return;const toggle=element.querySelector(".cx-mx-toggle");if(toggle&&toggle._group){this.toggleGroup(toggle._group.axis,toggle._group.key);return}
|
|
113
|
+
const link=element.querySelector("a[href]");if(link){link.click();return}
|
|
114
|
+
if(element.classList.contains("cx-mx-cell")){this.openCell(element);return}
|
|
115
|
+
const entry=element.classList.contains("cx-mx-hcell")&&this.model.columns[element._c];if(entry&&entry.band)this.toggleGroup("columns",entry.band.key)},toggleGroup(axis,key){const list=this.query.collapsed[axis],at=list.indexOf(key);if(at>=0)list.splice(at,1);else list.push(key);const entryKey="g"+key;this.derive(true);const index=(axis==="rows"?this.model.rows:this.model.columns).findIndex(entry=>entry.key===entryKey||entry.band&&entry.band.key===key);if(index>=0)this.moveTo(axis==="rows"?index:this.focus.row,axis==="rows"?-1:index);this.stateChanged()}});Object.assign(MatrixView.prototype,{buildBar(){const labels=this.labels,signal=this.lifetime.signal,bar=this.bar;const search=this.searchInput=h("input","cx-mx-input cx-mx-search");search.type="search";search.placeholder=labels.matrixSearch;search.setAttribute("aria-label",labels.matrixSearch);search.autocomplete="off";search.addEventListener("input",()=>{clearTimeout(this.searchTimer);this.searchTimer=setTimeout(()=>this.setQuery({search:search.value}),150)},{signal});this.axisButtons={};for(const axis of MATRIX_AXES){const button=h("button","cx-mx-button");button.type="button";button.setAttribute("aria-haspopup","dialog");button.setAttribute("aria-expanded","false");button.addEventListener("click",()=>this.togglePanel(axis),{signal});this.axisButtons[axis]=button}
|
|
116
|
+
const toggle=this.attentionToggle=h("input","cx-mx-check"),switchLabel=h("label","cx-mx-switch"),switchText=h("span");toggle.type="checkbox";setText(switchText,labels.matrixOnlyProblems);switchLabel.append(toggle,switchText);toggle.addEventListener("change",()=>this.setQuery({attentionOnly:toggle.checked}),{signal});this.attentionSwitch=switchLabel;this.attentionText=switchText;const empty=this.emptyToggle=h("input","cx-mx-check"),emptyLabel=this.emptySwitch=h("label","cx-mx-switch"),emptyText=this.emptyText=h("span");empty.type="checkbox";setText(emptyText,labels.matrixHideEmpty);emptyLabel.append(empty,emptyText);empty.addEventListener("change",()=>this.setQuery({hideEmpty:empty.checked}),{signal});const clear=this.clearButton=h("button","cx-mx-button cx-mx-clear");clear.type="button";setText(clear,labels.matrixClearFilters);clear.addEventListener("click",()=>{search.value="";this.setQuery({search:"",filters:{rows:{},columns:{}},attentionOnly:false});search.focus()},{signal});this.summary=h("span","cx-mx-summary");this.summary.setAttribute("aria-live","off");this.panel=h("div","cx-mx-panel");this.panel.setAttribute("role","dialog");this.panel.hidden=true;document.addEventListener("keydown",event=>{if(event.key==="Escape"&&!this.panel.hidden&&!event.defaultPrevented){event.preventDefault();this.closePanel(true)}},{signal});document.addEventListener("pointerdown",event=>{if(!this.panel.hidden&&!this.panel.contains(event.target)&&!Object.values(this.axisButtons).includes(event.target.closest("button")))this.closePanel(false)},{signal});bar.append(search,this.axisButtons.rows,this.axisButtons.columns,switchLabel,emptyLabel,clear,this.summary,this.panel)},refreshBar(){const labels=this.labels;this.searchInput.placeholder=labels.matrixSearch;this.searchInput.setAttribute("aria-label",labels.matrixSearch);setText(this.attentionText,labels.matrixOnlyProblems);setText(this.clearButton,labels.matrixClearFilters);this.attentionSwitch.hidden=!this.doc.states.some(state=>state.attention);setText(this.emptyText,labels.matrixHideEmpty);this.emptySwitch.hidden=!this.doc.sparse},axisTitle(axis){return axis==="rows"?this.doc.rows.title||this.labels.matrixRows:this.doc.columnTitle||this.labels.matrixColumns},activeFilters(axis){return Object.values(this.query.filters[axis]).reduce((sum,values)=>sum +(values&&values.length?1:0),0)},syncBar(){const query=this.query;for(const axis of MATRIX_AXES){const count=this.activeFilters(axis),button=this.axisButtons[axis];setText(button,this.axisTitle(axis)+(count?" ("+count+")":""));button.classList.toggle("cx-mx-active",!!count||!!query.group[axis])}
|
|
117
|
+
this.attentionToggle.checked=query.attentionOnly;this.emptyToggle.checked=query.hideEmpty;if(this.searchInput.value!==query.search&&document.activeElement!==this.searchInput)this.searchInput.value=query.search;this.clearButton.hidden=!(query.search||query.attentionOnly||this.activeFilters("rows")||this.activeFilters("columns"));setText(this.summary,this.summaryText());if(!this.panel.hidden)this.fillPanel(this.panelAxis)},summaryText(){const labels=this.labels,model=this.model,doc=this.doc,f=value=>this.format.format(value);const filtered=model.visible.rows!==doc.rows.items.length||model.visible.columns!==doc.columns.items.length;const size=filtered?fill(labels.matrixShown,{rows:f(model.visible.rows),totalRows:f(doc.rows.items.length),columns:f(model.visible.columns),totalColumns:f(doc.columns.items.length)}):
|
|
118
|
+
fill(labels.matrixSize,{rows:f(model.visible.rows),columns:f(model.visible.columns)});let attention=0;doc.states.forEach((state,index)=>{if(state.attention)attention +=model.counts[index]});return size +(doc.states.some(state=>state.attention)?" · "+fill(labels.matrixAttention,{count:f(attention)}):"")},paintLegend(){if(!this.doc.legend)return;const items=[],counts=this.model.counts;this.doc.states.forEach((state,index)=>{const item=h("span","cx-mx-key"),swatch=h("span","cx-mx-swatch" +(state.attention?" cx-mx-attn":"")),text=h("span"),count=h("span","cx-mx-keycount");item.setAttribute("role","listitem");applyTone(swatch,swatch.className,toneOf(state.tone));const symbol=matrixSymbol(state.symbol);if(symbol)swatch.appendChild(symbol);setText(text,state.label);setText(count,this.format.format(counts[index]));item.append(swatch,text,count);items.push(item)});const none=counts[this.doc.states.length];if(none){const item=h("span","cx-mx-key"),swatch=h("span","cx-mx-swatch cx-mx-none"),text=h("span"),count=h("span","cx-mx-keycount");item.setAttribute("role","listitem");setText(text,this.labels.matrixNoData);setText(count,this.format.format(none));item.append(swatch,text,count);items.push(item)}
|
|
119
|
+
this.legend.replaceChildren(...items)},togglePanel(axis){if(!this.panel.hidden&&this.panelAxis===axis){this.closePanel(true);return}
|
|
120
|
+
this.panelAxis=axis;this.fillPanel(axis);this.panel.hidden=false;for(const name of MATRIX_AXES)this.axisButtons[name].setAttribute("aria-expanded",String(name===axis));this.panel.querySelector("select,input")?.focus()},closePanel(returnFocus){if(this.panel.hidden)return;this.panel.hidden=true;for(const name of MATRIX_AXES)this.axisButtons[name].setAttribute("aria-expanded","false");if(returnFocus)this.axisButtons[this.panelAxis].focus()},fillPanel(axis){const labels=this.labels,doc=this.doc,data=doc[axis==="rows"?"rows":"columns"],query=this.query,panel=this.panel,signal=this.lifetime.signal;panel.setAttribute("aria-label",this.axisTitle(axis));const focused=panel.contains(document.activeElement)?document.activeElement.dataset.cxControl:null;const field=(text,control)=>{const label=h("label","cx-mx-field"),name=h("span");setText(name,text);label.append(name,control);return label};const select=(options,value,change,key)=>{const element=h("select","cx-mx-input");element.dataset.cxControl=key;for(const[optionValue,text]of options){const option=h("option");option.value=optionValue;setText(option,text);element.appendChild(option)}
|
|
121
|
+
element.value=value;element.addEventListener("change",()=>change(element.value),{signal});return element};const sort=query.sort[axis];const sortBy=select([["",labels.matrixAsListed],["#label",labels.matrixByLabel],["#total",labels.matrixByTotal],...data.attributes.map(attribute=>[attribute.id,attribute.title])],sort?sort.by:"",value=>this.sort(axis,value||null,value==="#total"?"desc":query.sort[axis]?.direction||"asc"),"sort");const descending=h("input","cx-mx-check");descending.type="checkbox";descending.checked=!!sort&&sort.direction==="desc";descending.disabled=!sort;descending.dataset.cxControl="direction";descending.addEventListener("change",()=>this.sort(axis,query.sort[axis]?.by||null,descending.checked?"desc":"asc"),{signal});const descendingLabel=h("label","cx-mx-switch"),descendingText=h("span");setText(descendingText,labels.matrixDescending);descendingLabel.append(descending,descendingText);const groupBy=select([["",labels.matrixNoGrouping],...data.attributes.map(attribute=>[attribute.id,attribute.title])],query.group[axis]||"",value=>this.group(axis,value||null),"group");const children=[field(labels.matrixSortBy,sortBy),descendingLabel];if(data.attributes.length)children.push(field(labels.matrixGroupBy,groupBy));for(const attribute of data.attributes.filter(item=>item.facet))children.push(this.facetList(axis,data,attribute));const close=h("button","cx-mx-button");close.type="button";setText(close,labels.matrixClose);close.dataset.cxControl="close";close.addEventListener("click",()=>this.closePanel(true),{signal});children.push(close);panel.replaceChildren(...children);if(focused)panel.querySelector('[data-cx-control="'+CSS.escape(focused)+ '"]')?.focus()},facetList(axis,data,attribute){const set=h("fieldset","cx-mx-facet"),legend=h("legend"),counts=new Map(),chosen=new Set(this.query.filters[axis][attribute.id]||[]),signal=this.lifetime.signal;setText(legend,attribute.title);set.appendChild(legend);for(const item of data.items){const key=matrixValueKey(item.values[attribute.id]);if(!counts.has(key))counts.set(key,{key,text:matrixValueText(item.values[attribute.id],attribute,this.format)||"—",count:0});counts.get(key).count++}
|
|
122
|
+
for(const value of[...counts.values()].sort((a,b)=>matrixNatural(a.text,b.text)).slice(0,200)){const label=h("label","cx-mx-switch"),box=h("input","cx-mx-check"),text=h("span"),count=h("span","cx-mx-keycount");box.type="checkbox";box.checked=chosen.has(value.key);box.dataset.cxControl="facet:"+attribute.id+":"+value.key;box.addEventListener("change",()=>{const next=new Set(this.query.filters[axis][attribute.id]||[]);if(box.checked)next.add(value.key);else next.delete(value.key);this.filter(axis,attribute.id,[...next])},{signal});setText(text,value.text);setText(count,this.format.format(value.count));label.append(box,text,count);set.appendChild(label)}
|
|
123
|
+
return set}});Object.assign(MatrixView.prototype,{cellInfo(cell){const row=this.model.rows[cell._r],column=this.model.columns[cell._c];if(!row||!column)return null;const info=this.model.cell(row,column),state=info.state<MATRIX_SELF?this.doc.states[info.state]:null;return{row,column,info,state}},cellLines(data){const labels=this.labels,lines=[],note=data.info.note;const stateText=data.state?data.state.label:data.info.state===MATRIX_SELF?labels.matrixSelf:labels.matrixNoData;lines.push([labels.matrixState,stateText]);if(note&¬e.value!=null)lines.push([labels.matrixValue,this.format.format(note.value)]);if(note&¬e.text)lines.push([note.value!=null?"":labels.matrixValue,note.text]);if(data.info.rolled){lines.push(["",fill(labels.matrixGroupCount,{count:this.format.format(data.info.members)})]);if(data.info.attention)lines.push(["",fill(labels.matrixAttention,{count:this.format.format(data.info.attention)})])}
|
|
124
|
+
if(note&¬e.title)lines.push(["",note.title]);for(const[label,value]of note&¬e.details||[])lines.push([label,value]);return lines},onPointer(event){const target=this.cellAt(event);if(target&&this.pointerCell&&target._r===this.pointerCell._r&&target._c===this.pointerCell._c)return;this.pointerCell=target;this.hoverCell(target);clearTimeout(this.tipTimer);if(!target){if(!this.tipKeyboard)this.hideTip();return}
|
|
125
|
+
this.tipTimer=setTimeout(()=>{if(!this.destroyed&&this.pointerCell===target)this.showTip(target,false)},250)},showTip(cell,keyboard){const data=this.cellInfo(cell);if(!data)return;this.tipKeyboard=keyboard;const tip=this.tip,title=h("div","cx-mx-tiptitle"),list=h("dl","cx-mx-tiplist");setText(title,data.row.label+" · "+data.column.label);for(const[label,value]of this.cellLines(data).slice(0,8)){const term=h("dt"),text=h("dd");setText(term,label);setText(text,value);list.append(term,text)}
|
|
126
|
+
tip.replaceChildren(title,list);tip.hidden=false;const box=this.cellRect(cell._r,cell._c),frame=this.root.getBoundingClientRect(),width=tip.offsetWidth,height=tip.offsetHeight;if(!box){this.hideTip();return}
|
|
127
|
+
let left=box.left-frame.left+box.width / 2 - width / 2, top = box.bottom - frame.top + 6;
|
|
128
|
+
left=Math.max(4,Math.min(left,frame.width-width-4));if(box.bottom+height+10>global.innerHeight&&box.top-height-6>0)top=box.top-frame.top-height-6;tip.style.left=left+"px";tip.style.top=top+"px"},hideTip(){clearTimeout(this.tipTimer);this.tip.hidden=true;this.tipKeyboard=false;this.pointerCell=null;this.hoverElement?.remove()},openCell(cell){const data=this.cellInfo(cell);if(!data)return;this.hideTip();const note=data.info.note,member=entry=>entry.kind==="item"?entry.item.id:null,group=entry=>entry.kind==="item"?null:entry.group.key;const detail=Object.freeze({matrix:this.doc.id,row:member(data.row),rowGroup:group(data.row),column:member(data.column),columnGroup:group(data.column),state:data.state?data.state.id:data.info.state===MATRIX_SELF?"self":null,value:note?note.value:null,text:note?note.text:null,title:note?note.title:null,details:note&¬e.details?note.details.map(pair=>[...pair]):[],members:data.info.members,attention:data.info.attention});if(!this.emit("matrixCell",detail,"onCell",true)||this.doc.details==="host")return;this.openDialog(data,cell)},openDialog(data,cell){const labels=this.labels,dialog=this.dialog||=this.buildDialog();setText(dialog._title,data.row.label+" · "+data.column.label);const rows=[];for(const[label,value]of this.cellLines(data)){const term=h("dt"),text=h("dd");setText(term,label);setText(text,value);rows.push(term,text)}
|
|
129
|
+
dialog._list.replaceChildren(...rows);const state=data.state,badge=dialog._state;badge.replaceChildren();applyTone(badge,"cx-mx-swatch" +(state&&state.attention?" cx-mx-attn":""),toneOf(state?state.tone:null));const symbol=state&&matrixSymbol(state.symbol);if(symbol)badge.appendChild(symbol);setText(dialog._close,labels.matrixClose);if(typeof dialog.showModal==="function")dialog.showModal();else dialog.setAttribute("open","");dialog._close.focus()},buildDialog(){const dialog=h("dialog","cx-mx-dialog"),head=h("div","cx-mx-dialoghead"),title=h("h2","cx-mx-dialogtitle"),state=h("span","cx-mx-swatch"),list=h("dl","cx-mx-details");const close=h("button","cx-mx-button"),signal=this.lifetime.signal;title.id=(this.host.id||"cx-matrix")+ "-"+Math.random().toString(36).slice(2)+ "-title";dialog.setAttribute("aria-labelledby",title.id);close.type="button";close.addEventListener("click",()=>dialog.close?dialog.close():dialog.removeAttribute("open"),{signal});dialog.addEventListener("close",()=>{if(!this.destroyed)this.moveTo(this.focus.row,this.focus.column,true,false)},{signal});head.append(state,title);dialog.append(head,list,close);dialog._title=title;dialog._list=list;dialog._close=close;dialog._state=state;this.root.appendChild(dialog);return dialog}});const MATRIX_GLYPHS={check:"✓",cross:"✕",warning:"!",info:"i",dot:"•",dash:"–",question:"?",clock:"◷",pause:"‖"};Object.assign(MatrixView.prototype,{attentionCells(row){const cells=[];for(const column of this.model.columns){const info=this.model.cell(row,column);if(info.state<MATRIX_SELF&&this.doc.attention[info.state])cells.push({column,info,state:this.doc.states[info.state]})}
|
|
130
|
+
return cells},renderCards(){const labels=this.labels,list=h("ul","cx-mx-cardlist"),limit=this.cardLimit||=100,rows=this.model.rows.filter(entry=>entry.kind!=="heading");let group=null;for(const entry of rows.slice(0,limit)){if(entry.band&&entry.band!==group){group=entry.band;const heading=h("li","cx-mx-cardgroup");setText(heading,group.label);list.appendChild(heading)}
|
|
131
|
+
const card=h("li","cx-mx-card"),head=h("div","cx-mx-cardhead"),title=h("span","cx-mx-label"),total=h("span","cx-mx-total" +(entry.total?"":" cx-mx-zero"));setText(title,entry.label);setText(total,this.format.format(entry.total||0));total.setAttribute("aria-label",fill(labels.matrixItemTotal,{item:entry.label,count:this.format.format(entry.total||0)}));head.append(title,total);card.appendChild(head);if(entry.kind==="item"&&entry.item.secondary){const sub=h("div","cx-mx-sub");setText(sub,entry.item.secondary);card.appendChild(sub)}
|
|
132
|
+
const cells=this.attentionCells(entry),items=h("ul","cx-mx-cardcells");for(const{column,info,state}of cells.slice(0,12)){const item=h("li","cx-mx-cardcell"),swatch=h("span","cx-mx-swatch cx-mx-attn"),text=h("span");applyTone(swatch,swatch.className,toneOf(state.tone));const symbol=matrixSymbol(state.symbol);if(symbol)swatch.appendChild(symbol);const note=info.note,extra=note&¬e.text?note.text:note&¬e.value!=null?this.format.format(note.value):"";setText(text,column.label+" · "+state.label +(extra?" · "+extra:""));item.append(swatch,text);items.appendChild(item)}
|
|
133
|
+
if(cells.length>12){const more=h("li","cx-mx-sub");setText(more,"+"+this.format.format(cells.length-12));items.appendChild(more)}
|
|
134
|
+
if(!cells.length){const clear=h("div","cx-mx-sub cx-mx-clear-text");setText(clear,labels.matrixAllClear);card.appendChild(clear)}else card.appendChild(items);list.appendChild(card)}
|
|
135
|
+
const children=[list];if(rows.length>limit){const more=h("button","cx-mx-button");more.type="button";setText(more,labels.matrixShowMore);more.addEventListener("click",()=>{this.cardLimit +=100;this.renderCards()},{signal:this.lifetime.signal});children.push(more)}
|
|
136
|
+
this.cards.replaceChildren(...children)},stateGlyph(info){if(info.state===MATRIX_SELF)return "";if(info.state===MATRIX_NONE)return "";const state=this.doc.states[info.state];return(MATRIX_GLYPHS[state.symbol]||"")+(info.note&&info.note.text?" "+info.note.text:info.note&&info.note.value!=null?" "+this.format.format(info.note.value):"")},renderPrint(){if(this.destroyed)return;const model=this.model,labels=this.labels,table=h("table","cx-mx-printtable"),caption=h("caption");setText(caption,this.doc.label+" — "+this.summaryText());table.appendChild(caption);const columns=model.columns,rows=model.rows.filter(entry=>entry.kind!=="heading");if(rows.length*columns.length<=2500){const head=h("tr"),corner=h("th");head.appendChild(corner);for(const column of columns){const th=h("th");th.scope="col";setText(th,column.label);head.appendChild(th)}
|
|
137
|
+
if(this.totalW){const th=h("th");setText(th,labels.matrixTotal);head.appendChild(th)}
|
|
138
|
+
table.appendChild(head);for(const row of rows){const tr=h("tr"),th=h("th");th.scope="row";setText(th,row.label);tr.appendChild(th);for(const column of columns){const info=model.cell(row,column),td=h("td"),state=info.state<MATRIX_SELF?this.doc.states[info.state]:null;setText(td,this.stateGlyph(info));td.title=state?state.label:info.state===MATRIX_SELF?labels.matrixSelf:labels.matrixNoData;if(state&&state.attention)td.className="cx-mx-printattn";tr.appendChild(td)}
|
|
139
|
+
if(this.totalW){const td=h("td");setText(td,this.format.format(row.total||0));tr.appendChild(td)}
|
|
140
|
+
table.appendChild(tr)}}else{const head=h("tr");for(const text of[this.axisTitle("rows"),this.axisTitle("columns"),labels.matrixState,labels.matrixValue]){const th=h("th");th.scope="col";setText(th,text);head.appendChild(th)}
|
|
141
|
+
table.appendChild(head);let written=0,left=0;for(const row of rows){for(const{column,info,state}of this.attentionCells(row)){if(written>=5000){left++;continue}
|
|
142
|
+
written++;const tr=h("tr");for(const text of[row.label,column.label,state.label,info.note&&(info.note.text||(info.note.value!=null?this.format.format(info.note.value):""))||""]){const td=h("td");setText(td,text);tr.appendChild(td)}
|
|
143
|
+
table.appendChild(tr)}}
|
|
144
|
+
if(left){const tr=h("tr"),td=h("td");td.colSpan=4;setText(td,"+"+this.format.format(left));tr.appendChild(td);table.appendChild(tr)}}
|
|
145
|
+
this.print.replaceChildren(table)}});function matrixAxisName(axis){if(!MATRIX_AXES.includes(axis))throw new Error("CanopyX: a matrix axis is \"rows\" or \"columns\".");return axis}
|
|
146
|
+
Object.assign(MatrixView.prototype,{axisData(axis){return matrixAxisName(axis)==="rows"?this.doc.rows:this.doc.columns},checkSort(axis,sort){if(sort==null)return null;if(typeof sort!=="object"||(sort.by!=="#label"&&sort.by!=="#total"&&!this.axisData(axis).attributeById.has(sort.by))||![undefined,"asc","desc"].includes(sort.direction))
|
|
147
|
+
throw new Error("CanopyX: a matrix sort is { by: \"#label\" | \"#total\" | an attribute, direction: \"asc\" | \"desc\" }.");return{by:sort.by,direction:sort.direction||"asc"}},checkFilters(axis,filters){const data=this.axisData(axis),result=Object.create(null);if(filters==null)return result;if(typeof filters!=="object"||Array.isArray(filters))throw new Error("CanopyX: matrix filters are values by attribute id.");for(const[id,values]of Object.entries(filters)){if(!data.attributeById.has(id)||values!==null&&(!Array.isArray(values)||values.length>200||values.some(value=>typeof value!=="string")))throw new Error("CanopyX: invalid matrix filter on '"+id+"'.");if(values&&values.length)result[id]=[...new Set(values)]}
|
|
148
|
+
return result},applyState(state,derive=true){if(!state||typeof state!=="object")throw new Error("CanopyX: a matrix state is an object.");const query=this.query,next={...query,filters:{...query.filters},sort:{...query.sort},group:{...query.group},collapsed:{rows:[...query.collapsed.rows],columns:[...query.collapsed.columns]}};if(state.search!==undefined){if(typeof state.search!=="string"||state.search.length>256)throw new Error("CanopyX: a matrix search is text of at most 256 characters.");next.search=state.search}
|
|
149
|
+
if(state.attentionOnly!==undefined)next.attentionOnly=!!state.attentionOnly;if(state.hideEmpty!==undefined)next.hideEmpty=!!state.hideEmpty;for(const axis of MATRIX_AXES){if(state.filters&&state.filters[axis]!==undefined)next.filters[axis]=this.checkFilters(axis,state.filters[axis]);if(state.sort&&state.sort[axis]!==undefined)next.sort[axis]=this.checkSort(axis,state.sort[axis]);if(state.group&&state.group[axis]!==undefined){const group=state.group[axis];if(group!==null&&!this.axisData(axis).attributeById.has(group))throw new Error("CanopyX: a matrix groups by one of its attributes.");if(group!==next.group[axis])next.collapsed[axis]=[];next.group[axis]=group}
|
|
150
|
+
if(state.collapsed&&state.collapsed[axis]!==undefined){if(!Array.isArray(state.collapsed[axis])||state.collapsed[axis].some(key=>typeof key!=="string"))throw new Error("CanopyX: collapsed groups are a list of group keys.");next.collapsed[axis]=[...new Set(state.collapsed[axis])]}}
|
|
151
|
+
this.query=next;if(derive){this.derive(true);this.stateChanged()}},setQuery(partial){this.applyState(partial)},sort(axis,by,direction="asc"){this.applyState({sort:{[matrixAxisName(axis)]:by==null?null:{by,direction}}})},group(axis,attribute){this.applyState({group:{[matrixAxisName(axis)]:attribute??null}})},filter(axis,attribute,values){this.applyState({filters:{[matrixAxisName(axis)]:{...this.query.filters[axis],[attribute]:values??null}}})},getState(){const query=this.query,copy=value=>JSON.parse(JSON.stringify(value));return gridFreeze({search:query.search,attentionOnly:query.attentionOnly,hideEmpty:query.hideEmpty,filters:copy(query.filters),sort:copy(query.sort),group:copy(query.group),collapsed:copy(query.collapsed)})},stateChanged(){this.emit("matrixState",this.getState(),"onStateChange")},getVisible(){const ids=entries=>entries.filter(entry=>entry.kind!=="heading").flatMap(entry=>entry.members.map(item=>item.id));return gridFreeze({rows:ids(this.model.rows),columns:ids(this.model.columns)})},focusCell(row,column){const r=this.model.rows.findIndex(entry=>entry.kind!=="heading"&&entry.members.some(item=>item.id===row));const c=this.model.columns.findIndex(entry=>entry.members.some(item=>item.id===column));if(r<0||c<0)return false;this.moveTo(r,c);return true},update(view){const doc=matrixDocument(view),state=JSON.parse(JSON.stringify(this.getState())),focus=this.captureFocus();this.doc=doc;this.labels=matrixLabels(doc.labels,this.options.labels);this.model=new MatrixModel(doc,this.format);this.query=this.model.initialQuery();for(const axis of MATRIX_AXES){const attributes=this.axisData(axis).attributeById;state.filters[axis]=Object.fromEntries(Object.entries(state.filters[axis]).filter(([id])=>attributes.has(id)));const sort=state.sort[axis];if(sort&&!["#label","#total"].includes(sort.by)&&!attributes.has(sort.by))state.sort[axis]=null;if(state.group[axis]!==null&&!attributes.has(state.group[axis])){state.group[axis]=null;state.collapsed[axis]=[]}}
|
|
152
|
+
this.applyState(state,false);this.root.className="cx-matrix cx-mx-"+doc.density;this.source=view;this.closePanel(false);this.refreshChrome();this.geometry=undefined;this.derive(false,focus)}});function matrixCreate(host,options){const view=new MatrixView(host,options);const impl={ready:Promise.resolve().then(()=>{if(view.destroyed)return impl;host.setAttribute("data-cx-ready","");view.emit("matrixReady",{matrix:view.doc.id},"onReady");return impl}),document:()=>view.source||view.options.view,sort:(axis,by,direction)=>view.sort(axis,by,direction),group:(axis,attribute)=>view.group(axis,attribute),filter:(axis,attribute,values)=>view.filter(axis,attribute,values),setSearch:text=>view.setQuery({search:text}),setAttentionOnly:on=>view.setQuery({attentionOnly:!!on}),getState:()=>view.getState(),setState:state=>view.applyState(state),getVisible:()=>view.getVisible(),focusCell:(row,column)=>view.focusCell(row,column),update:next=>view.update(next),destroy:()=>{if(view.destroyed)return;view.destroyed=true;view.lifetime.abort();view.resize?.disconnect();view.themeWatch?.disconnect();cancelAnimationFrame(view.frame);cancelAnimationFrame(view.fitFrame);clearTimeout(view.searchTimer);clearTimeout(view.tipTimer);view.root.remove();host.removeAttribute("data-cx-ready");if(view.addedTheme)host.removeAttribute("data-cx-theme");if(view.fitInline!==undefined){host.style.maxHeight=view.fitInline;if(!host.getAttribute("style"))host.removeAttribute("style")}}};return impl}
|
|
153
|
+
MATRIX_IMPL.create=matrixCreate;installed.add(context.MATRIX_IMPL)}};(global.CanopyXParts||=[]).push(["matrix",part])})(typeof window!=="undefined"?window:globalThis);
|
|
@@ -62,7 +62,7 @@ function fill(template,values){return template.replace(/\{(\w+)\}/g, (match, nam
|
|
|
62
62
|
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];
|
|
63
63
|
let rules=pluralRules.get(lang);if(!rules){try{rules=new Intl.PluralRules(lang||"en")}catch(_){rules=new Intl.PluralRules("en")}pluralRules.set(lang,rules)}
|
|
64
64
|
return forms[rules.select(Number(count))]??forms.other??template}
|
|
65
|
-
let DEFAULT_LABELS=Object.freeze({searchPlaceholder:"Search",searchResults:"Search results",searchResultCount:"{count} results",searchResultCountOfTotal:"{count} of {total} results",searchResultCountOfMany:"{count} of many results",searchNoResults:"No results",searchTooShort:"Type at least {min} characters",searchWordTooShort:"Type a word of at least {min} characters",searchFailed:"Search failed: {error}",columns:"Columns",visibleColumns:"Visible columns",resizeColumn:"Resize {column}",columnWidth:"{width} pixels",loading:"Loading…",loadError:"Could not load: {error} (Enter to retry)",queryFailed:"Could not update the view: {error}",showMore:"Show more ({loaded} of {total} loaded)",showMoreUnknownTotal:"Show more ({loaded} loaded)",pageLoaded:"{loaded} of {total} loaded",itemsLoaded:"{count} items",yes:"Yes",no:"No",utcTime:"{time} UTC",localTime:"{time} local time",mountError:"This view could not be displayed: {error}"});function labelsFor(...sources){const labels=Object.assign({},DEFAULT_LABELS);for(const source of sources){if(source==null)continue;if(typeof source!=="object"||Array.isArray(source))throw new Error("CanopyX: labels must be an object of strings.");Object.keys(source).forEach((name)=>{if(!Object.hasOwn(DEFAULT_LABELS,name)&&!/^(?:gridEdit|format|calendar|flow)[A-Z]\w{0,63}$/.test(name)) throw new Error("CanopyX: there is no label '" + name + "'.");
|
|
65
|
+
let DEFAULT_LABELS=Object.freeze({searchPlaceholder:"Search",searchResults:"Search results",searchResultCount:"{count} results",searchResultCountOfTotal:"{count} of {total} results",searchResultCountOfMany:"{count} of many results",searchNoResults:"No results",searchTooShort:"Type at least {min} characters",searchWordTooShort:"Type a word of at least {min} characters",searchFailed:"Search failed: {error}",columns:"Columns",visibleColumns:"Visible columns",resizeColumn:"Resize {column}",columnWidth:"{width} pixels",loading:"Loading…",loadError:"Could not load: {error} (Enter to retry)",queryFailed:"Could not update the view: {error}",showMore:"Show more ({loaded} of {total} loaded)",showMoreUnknownTotal:"Show more ({loaded} loaded)",pageLoaded:"{loaded} of {total} loaded",itemsLoaded:"{count} items",yes:"Yes",no:"No",utcTime:"{time} UTC",localTime:"{time} local time",mountError:"This view could not be displayed: {error}"});function labelsFor(...sources){const labels=Object.assign({},DEFAULT_LABELS);for(const source of sources){if(source==null)continue;if(typeof source!=="object"||Array.isArray(source))throw new Error("CanopyX: labels must be an object of strings.");Object.keys(source).forEach((name)=>{if(!Object.hasOwn(DEFAULT_LABELS,name)&&!/^(?:gridEdit|format|calendar|flow|matrix)[A-Z]\w{0,63}$/.test(name)) throw new Error("CanopyX: there is no label '" + name + "'.");
|
|
66
66
|
if(typeof source[name]!=="string"||!/\S/.test(source[name])) throw new Error("CanopyX: label '" + name + "' must be a string with visible text.");
|
|
67
67
|
if((name==="utcTime"||name==="localTime")&&source[name].indexOf("{time}")<0)throw new Error("CanopyX: label '"+name+"' must hold {time}.");labels[name]=source[name]})}
|
|
68
68
|
return Object.freeze(labels)}
|
|
@@ -506,7 +506,7 @@ if(active>=0){list.children[active].setAttribute("aria-selected","false")}
|
|
|
506
506
|
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=""}
|
|
507
507
|
close()}else{return}
|
|
508
508
|
e.preventDefault()});input.addEventListener("blur",()=>{cancelNavigation();sequence++;clearTimeout(timer);clearTimeout(blurTimer);blurTimer=setTimeout(close,150)});this.cancelSearch=()=>{cancelNavigation();sequence++;clearTimeout(timer);clearTimeout(blurTimer);hits=[];close()};box.appendChild(input);box.appendChild(list);box.appendChild(status);bar.appendChild(box);this.searchInput=input}});function watchFlyout(anchorElement,panel,isOpen){const position=()=>{if(!isOpen())return;const anchor=anchorElement.getBoundingClientRect(),fixed=getComputedStyle(panel).position==="fixed";const below=Math.max(0,global.innerHeight-anchor.bottom-8);const above=Math.max(0,anchor.top-8);const opensAbove=below<120&&above>below,room=opensAbove?above:below;panel.style.maxHeight=(panel.classList.contains("cx-fb")?room:Math.min(320,room))+ "px";panel.style.top=opensAbove?"auto":fixed?anchor.bottom+"px":"100%";panel.style.bottom=opensAbove?fixed?global.innerHeight-anchor.top+"px":"100%":"auto";const width=panel.getBoundingClientRect().width;panel.style.left=Math.max(8,Math.min(anchor.left,global.innerWidth-width-8))-(fixed?0:anchor.left)+ "px"};global.addEventListener("resize",position);global.addEventListener("scroll",position,true);return{position,dispose:()=>{global.removeEventListener("resize",position);global.removeEventListener("scroll",position,true)}}}
|
|
509
|
-
const PART_URL=typeof document!=="undefined"&&document.currentScript?.src;const PARTS={"tree-tools":["
|
|
509
|
+
const PART_URL=typeof document!=="undefined"&&document.currentScript?.src;const PARTS={"tree-tools":["dbc7cd1bfd91ce63","canopyx-tree-tools.879d4297cd1760d6.js","canopyx-tree-tools.ef7a3fb9c9115a82.css"],"export":["8b99c0b7b0ec37c5","canopyx-export.6989ea36f5d6da7b.js"],"links":["f1ab7e32b501ce37","canopyx-links.d96addeefb3eccf5.js"],"search":["521eebd6e1bbbd35","canopyx-search.14c69f4551c69f23.js"],"link":["fa0c9e888463553b","canopyx-link.4ec39bc39fa6cf50.js","canopyx-link.8435c892613926ac.css"],"highlight":["f39423961654c1e2","canopyx-highlight.a6f93893d36ab343.js","canopyx-highlight.5b6d2e0d979f9f9b.css"],"layouts":["fa8c686f21626884","canopyx-layouts.e7250c82a6abf52e.js","canopyx-layouts.0b75738924533ee8.css"],"record-panel":["bcc06276c7933af6","canopyx-record-panel.a374fb4254f9115c.js","canopyx-record-panel.f3691d54cefaab04.css"]},PART_CODE=new Map(),partName=name=>typeof name==="string"&&/^[a-z][a-z\d-]{0,31}$/.test(name);
|
|
510
510
|
function registerPart(name,part){if(!partName(name)||!part||typeof part.version!=="string"||typeof part.install!=="function"||
|
|
511
511
|
part.requires!==undefined&&!(Array.isArray(part.requires)&&part.requires.every(partName)))throw new Error("CanopyX: invalid part.");PART_CODE.set(name,part)}
|
|
512
512
|
function definePart(View,name,label,context){const parts=View.prototype.parts||={};return parts[name]={name,label,context,parts}}
|
|
@@ -742,5 +742,5 @@ watcher.observe(host)}
|
|
|
742
742
|
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}
|
|
743
743
|
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}}
|
|
744
744
|
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}
|
|
745
|
-
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";const create=flow?FLOW_API.createFlow: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"&&activeGrids.get(host)===api&&host.canopyx===api)showError(host,err,api,view)})}catch(err){showError(host,err,null,view)}});return created}
|
|
746
|
-
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:{}));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,configure,unconfigure,mount,sources,badge,labels,registerExportFormat,use,registerPart,setNonce}=CanopyX;export{protocol,createTreeGrid,configure,unconfigure,mount,sources,badge,labels,registerExportFormat,use,registerPart,setNonce};export default CanopyX;
|
|
745
|
+
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 create=flow?FLOW_API.createFlow:matrix?MATRIX_API.createMatrix: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&&activeGrids.get(host)===api&&host.canopyx===api)showError(host,err,api,view)})}catch(err){showError(host,err,null,view)}});return created}
|
|
746
|
+
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:{}));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,configure,unconfigure,mount,sources,badge,labels,registerExportFormat,use,registerPart,setNonce}=CanopyX;export{protocol,createTreeGrid,configure,unconfigure,mount,sources,badge,labels,registerExportFormat,use,registerPart,setNonce};export default CanopyX;
|