@evotecit/canopyx 0.3.3 → 0.3.5
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
|
@@ -60,7 +60,7 @@ function fill(template,values){return template.replace(/\{(\w+)\}/g, (match, nam
|
|
|
60
60
|
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];
|
|
61
61
|
let rules=pluralRules.get(lang);if(!rules){try{rules=new Intl.PluralRules(lang||"en")}catch(_){rules=new Intl.PluralRules("en")}pluralRules.set(lang,rules)}
|
|
62
62
|
return forms[rules.select(Number(count))]??forms.other??template}
|
|
63
|
-
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 + "'.");
|
|
63
|
+
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 + "'.");
|
|
64
64
|
if(typeof source[name]!=="string"||!/\S/.test(source[name])) throw new Error("CanopyX: label '" + name + "' must be a string with visible text.");
|
|
65
65
|
if((name==="utcTime"||name==="localTime")&&source[name].indexOf("{time}")<0)throw new Error("CanopyX: label '"+name+"' must hold {time}.");labels[name]=source[name]})}
|
|
66
66
|
return Object.freeze(labels)}
|
|
@@ -504,7 +504,7 @@ if(active>=0){list.children[active].setAttribute("aria-selected","false")}
|
|
|
504
504
|
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=""}
|
|
505
505
|
close()}else{return}
|
|
506
506
|
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)}}}
|
|
507
|
-
const PART_URL=typeof document!=="undefined"&&document.currentScript?.src;const PARTS={"tree-tools":["
|
|
507
|
+
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);
|
|
508
508
|
function registerPart(name,part){if(!partName(name)||!part||typeof part.version!=="string"||typeof part.install!=="function"||
|
|
509
509
|
part.requires!==undefined&&!(Array.isArray(part.requires)&&part.requires.every(partName)))throw new Error("CanopyX: invalid part.");PART_CODE.set(name,part)}
|
|
510
510
|
function definePart(View,name,label,context){const parts=View.prototype.parts||={};return parts[name]={name,label,context,parts}}
|
|
@@ -610,5 +610,5 @@ watcher.observe(host)}
|
|
|
610
610
|
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}
|
|
611
611
|
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}}
|
|
612
612
|
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}
|
|
613
|
-
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}
|
|
614
|
-
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:{}));const navigation=global.performance&&performance.getEntriesByType?performance.getEntriesByType("navigation")[0]:null;if(document.readyState==="loading"||(document.readyState==="interactive"&&navigation&&navigation.domContentLoadedEventStart===0)){document.addEventListener("DOMContentLoaded",()=>mount(document,true))}else{mount(document,true)}})(typeof window!=="undefined"?window:this);
|
|
613
|
+
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}
|
|
614
|
+
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:{}));const navigation=global.performance&&performance.getEntriesByType?performance.getEntriesByType("navigation")[0]:null;if(document.readyState==="loading"||(document.readyState==="interactive"&&navigation&&navigation.domContentLoadedEventStart===0)){document.addEventListener("DOMContentLoaded",()=>mount(document,true))}else{mount(document,true)}})(typeof window!=="undefined"?window:this);
|
package/JavaScript/grid.d.ts
CHANGED
|
@@ -931,17 +931,20 @@ export function createDataGrid<Row extends GridRow = GridRow>(host: HTMLElement,
|
|
|
931
931
|
export function createDataGrid<Row extends GridRow = GridRow>(host: HTMLElement, options: CursorGridOptions<GridNamedRow<Row>>): DataGrid<GridCursorQuery, Row>;
|
|
932
932
|
/** A calendar from a list of events, with no grid schema to write: a preset (basic by default) and options over it (calendar.d.ts). */
|
|
933
933
|
export function createCalendar(host: HTMLElement, options?: CalendarOptions): DataGrid<GridQuery, GridRow>;
|
|
934
|
-
/** Registers options before a tree, flat-grid or
|
|
935
|
-
export function configure(viewId: string, options: HostOptions | GridHostOptions | GridHostOptions<GridCursorQuery> | Partial<Omit<FlowOptions, "flow">>): () => void;
|
|
934
|
+
/** Registers options before a tree, flat-grid, flow or matrix fragment mounts. */
|
|
935
|
+
export function configure(viewId: string, options: HostOptions | GridHostOptions | GridHostOptions<GridCursorQuery> | Partial<Omit<FlowOptions, "flow">> | Partial<Omit<MatrixOptions, "view">>): () => void;
|
|
936
936
|
/**
|
|
937
|
-
* Mounts tree, flat-grid and
|
|
937
|
+
* Mounts tree, flat-grid, flow and matrix fragments below root, or root itself when it is a fragment. Fragments marked
|
|
938
938
|
* `data-cx-mount="visible"` below root wait until they come near the viewport; passing such a fragment as root mounts it at once.
|
|
939
939
|
* Narrow a controller before using view-specific methods.
|
|
940
940
|
*/
|
|
941
|
-
export function mount(root?: ParentNode): Array<TreeGrid | DataGrid<AcceptedGridQuery> | FlowApi>;
|
|
941
|
+
export function mount(root?: ParentNode): Array<TreeGrid | DataGrid<AcceptedGridQuery> | FlowApi | MatrixApi>;
|
|
942
942
|
|
|
943
943
|
import type { createFlow, FlowApi, FlowOptions } from "./flow.js";
|
|
944
944
|
export { createFlow } from "./flow.js";
|
|
945
945
|
export type { FlowDocument, FlowField, FlowStep, FlowSection, FlowOption, FlowCondition, FlowValidation, FlowValue, FlowSubmission, FlowSubmitDetail, FlowAnswer, FlowHooks, FlowOptions, FlowApi, FlowState, FlowValidationResult } from "./flow.js";
|
|
946
|
-
|
|
946
|
+
import type { createMatrix, MatrixOptions, MatrixApi } from "./matrix.js";
|
|
947
|
+
export { createMatrix } from "./matrix.js";
|
|
948
|
+
export type { MatrixDocument, MatrixAxis, MatrixItem, MatrixAttribute, MatrixState, MatrixSort, MatrixNote, MatrixQueryState, MatrixStateInput, MatrixCellDetail, MatrixOptions, MatrixApi } from "./matrix.js";
|
|
949
|
+
declare const gridRuntime: Omit<typeof CanopyX, "configure" | "mount"> & { createDataGrid: typeof createDataGrid; createCalendar: typeof createCalendar; createFlow: typeof createFlow; createMatrix: typeof createMatrix; enhance: typeof enhance; grid: typeof grid; configure: typeof configure; mount: typeof mount };
|
|
947
950
|
export default gridRuntime;
|
|
@@ -588,6 +588,39 @@ const labels = Object.freeze({
|
|
|
588
588
|
flowCopied: "Kopiert",
|
|
589
589
|
flowSavedIt: "Ich habe es gespeichert",
|
|
590
590
|
flowShownOnce: "Wird nur einmal angezeigt. Jetzt kopieren.",
|
|
591
|
+
matrixRows: "Zeilen",
|
|
592
|
+
matrixColumns: "Spalten",
|
|
593
|
+
matrixSearch: "Zeilen und Spalten suchen",
|
|
594
|
+
matrixSortBy: "Sortieren nach",
|
|
595
|
+
matrixGroupBy: "Gruppieren nach",
|
|
596
|
+
matrixByLabel: "Name",
|
|
597
|
+
matrixByTotal: "Gesamt",
|
|
598
|
+
matrixAsListed: "Wie aufgeführt",
|
|
599
|
+
matrixNoGrouping: "Keine Gruppen",
|
|
600
|
+
matrixDescending: "Absteigend",
|
|
601
|
+
matrixOnlyProblems: "Nur Probleme",
|
|
602
|
+
matrixHideEmpty: "Leere Zeilen und Spalten ausblenden",
|
|
603
|
+
matrixSize: "{rows} × {columns}",
|
|
604
|
+
matrixShown: "Zeilen: {rows} von {totalRows}, Spalten: {columns} von {totalColumns}",
|
|
605
|
+
matrixAttention: "Erfordert Aufmerksamkeit: {count}",
|
|
606
|
+
matrixLegend: "Zustände",
|
|
607
|
+
matrixFilter: "Filtern",
|
|
608
|
+
matrixClearFilters: "Filter löschen",
|
|
609
|
+
matrixNoMatches: "Keine Zeilen oder Spalten passen.",
|
|
610
|
+
matrixSelf: "Dasselbe Element",
|
|
611
|
+
matrixNoData: "Keine Daten",
|
|
612
|
+
matrixCell: "{row} und {column}: {state}",
|
|
613
|
+
matrixTotal: "Gesamt",
|
|
614
|
+
matrixItemTotal: "{item}: {count}",
|
|
615
|
+
matrixExpandGroup: "{group} aufklappen",
|
|
616
|
+
matrixCollapseGroup: "{group} zuklappen",
|
|
617
|
+
matrixGroupCount: "Elemente: {count}",
|
|
618
|
+
matrixDetails: "Details",
|
|
619
|
+
matrixClose: "Schließen",
|
|
620
|
+
matrixValue: "Wert",
|
|
621
|
+
matrixState: "Zustand",
|
|
622
|
+
matrixAllClear: "Nichts erfordert Aufmerksamkeit",
|
|
623
|
+
matrixShowMore: "Mehr anzeigen",
|
|
591
624
|
});
|
|
592
625
|
const staticLabels = Object.freeze({
|
|
593
626
|
overflowNote: "{shown} Zeilen angezeigt. Gesamtzahl: {total}.",
|
|
@@ -588,6 +588,39 @@ const labels = Object.freeze({
|
|
|
588
588
|
flowCopied: "Copiado",
|
|
589
589
|
flowSavedIt: "Lo he guardado",
|
|
590
590
|
flowShownOnce: "Se muestra solo una vez. Cópielo ahora.",
|
|
591
|
+
matrixRows: "Filas",
|
|
592
|
+
matrixColumns: "Columnas",
|
|
593
|
+
matrixSearch: "Buscar filas y columnas",
|
|
594
|
+
matrixSortBy: "Ordenar por",
|
|
595
|
+
matrixGroupBy: "Agrupar por",
|
|
596
|
+
matrixByLabel: "Nombre",
|
|
597
|
+
matrixByTotal: "Total",
|
|
598
|
+
matrixAsListed: "Como en la lista",
|
|
599
|
+
matrixNoGrouping: "Sin grupos",
|
|
600
|
+
matrixDescending: "Descendente",
|
|
601
|
+
matrixOnlyProblems: "Solo problemas",
|
|
602
|
+
matrixHideEmpty: "Ocultar filas y columnas vacías",
|
|
603
|
+
matrixSize: "{rows} × {columns}",
|
|
604
|
+
matrixShown: "Filas: {rows} de {totalRows}, columnas: {columns} de {totalColumns}",
|
|
605
|
+
matrixAttention: "Requieren atención: {count}",
|
|
606
|
+
matrixLegend: "Estados",
|
|
607
|
+
matrixFilter: "Filtrar",
|
|
608
|
+
matrixClearFilters: "Borrar filtros",
|
|
609
|
+
matrixNoMatches: "Ninguna fila o columna coincide.",
|
|
610
|
+
matrixSelf: "El mismo elemento",
|
|
611
|
+
matrixNoData: "Sin datos",
|
|
612
|
+
matrixCell: "{row} y {column}: {state}",
|
|
613
|
+
matrixTotal: "Total",
|
|
614
|
+
matrixItemTotal: "{item}: {count}",
|
|
615
|
+
matrixExpandGroup: "Expandir {group}",
|
|
616
|
+
matrixCollapseGroup: "Contraer {group}",
|
|
617
|
+
matrixGroupCount: "Elementos: {count}",
|
|
618
|
+
matrixDetails: "Detalles",
|
|
619
|
+
matrixClose: "Cerrar",
|
|
620
|
+
matrixValue: "Valor",
|
|
621
|
+
matrixState: "Estado",
|
|
622
|
+
matrixAllClear: "Nada requiere atención",
|
|
623
|
+
matrixShowMore: "Mostrar más",
|
|
591
624
|
});
|
|
592
625
|
const staticLabels = Object.freeze({
|
|
593
626
|
overflowNote: "Filas mostradas: {shown}. Total: {total}.",
|
|
@@ -588,6 +588,39 @@ const labels = Object.freeze({
|
|
|
588
588
|
flowCopied: "Copié",
|
|
589
589
|
flowSavedIt: "Je l'ai enregistré",
|
|
590
590
|
flowShownOnce: "Affiché une seule fois. Copiez-le maintenant.",
|
|
591
|
+
matrixRows: "Lignes",
|
|
592
|
+
matrixColumns: "Colonnes",
|
|
593
|
+
matrixSearch: "Rechercher des lignes et des colonnes",
|
|
594
|
+
matrixSortBy: "Trier par",
|
|
595
|
+
matrixGroupBy: "Regrouper par",
|
|
596
|
+
matrixByLabel: "Nom",
|
|
597
|
+
matrixByTotal: "Total",
|
|
598
|
+
matrixAsListed: "Comme listé",
|
|
599
|
+
matrixNoGrouping: "Aucun groupe",
|
|
600
|
+
matrixDescending: "Décroissant",
|
|
601
|
+
matrixOnlyProblems: "Problèmes uniquement",
|
|
602
|
+
matrixHideEmpty: "Masquer les lignes et colonnes vides",
|
|
603
|
+
matrixSize: "{rows} × {columns}",
|
|
604
|
+
matrixShown: "Lignes : {rows} sur {totalRows}, colonnes : {columns} sur {totalColumns}",
|
|
605
|
+
matrixAttention: "À examiner : {count}",
|
|
606
|
+
matrixLegend: "États",
|
|
607
|
+
matrixFilter: "Filtrer",
|
|
608
|
+
matrixClearFilters: "Effacer les filtres",
|
|
609
|
+
matrixNoMatches: "Aucune ligne ni colonne ne correspond.",
|
|
610
|
+
matrixSelf: "Même élément",
|
|
611
|
+
matrixNoData: "Aucune donnée",
|
|
612
|
+
matrixCell: "{row} et {column} : {state}",
|
|
613
|
+
matrixTotal: "Total",
|
|
614
|
+
matrixItemTotal: "{item} : {count}",
|
|
615
|
+
matrixExpandGroup: "Développer {group}",
|
|
616
|
+
matrixCollapseGroup: "Réduire {group}",
|
|
617
|
+
matrixGroupCount: "Éléments : {count}",
|
|
618
|
+
matrixDetails: "Détails",
|
|
619
|
+
matrixClose: "Fermer",
|
|
620
|
+
matrixValue: "Valeur",
|
|
621
|
+
matrixState: "État",
|
|
622
|
+
matrixAllClear: "Rien à examiner",
|
|
623
|
+
matrixShowMore: "Afficher plus",
|
|
591
624
|
});
|
|
592
625
|
const staticLabels = Object.freeze({
|
|
593
626
|
overflowNote: "{shown} lignes affichées sur {total}.",
|
|
@@ -588,6 +588,39 @@ const labels = Object.freeze({
|
|
|
588
588
|
flowCopied: "Copiato",
|
|
589
589
|
flowSavedIt: "L'ho salvato",
|
|
590
590
|
flowShownOnce: "Mostrato una sola volta. Copialo ora.",
|
|
591
|
+
matrixRows: "Righe",
|
|
592
|
+
matrixColumns: "Colonne",
|
|
593
|
+
matrixSearch: "Cerca righe e colonne",
|
|
594
|
+
matrixSortBy: "Ordina per",
|
|
595
|
+
matrixGroupBy: "Raggruppa per",
|
|
596
|
+
matrixByLabel: "Nome",
|
|
597
|
+
matrixByTotal: "Totale",
|
|
598
|
+
matrixAsListed: "Come elencato",
|
|
599
|
+
matrixNoGrouping: "Nessun gruppo",
|
|
600
|
+
matrixDescending: "Decrescente",
|
|
601
|
+
matrixOnlyProblems: "Solo problemi",
|
|
602
|
+
matrixHideEmpty: "Nascondi righe e colonne vuote",
|
|
603
|
+
matrixSize: "{rows} × {columns}",
|
|
604
|
+
matrixShown: "Righe: {rows} di {totalRows}, colonne: {columns} di {totalColumns}",
|
|
605
|
+
matrixAttention: "Richiedono attenzione: {count}",
|
|
606
|
+
matrixLegend: "Stati",
|
|
607
|
+
matrixFilter: "Filtra",
|
|
608
|
+
matrixClearFilters: "Cancella filtri",
|
|
609
|
+
matrixNoMatches: "Nessuna riga o colonna corrisponde.",
|
|
610
|
+
matrixSelf: "Stesso elemento",
|
|
611
|
+
matrixNoData: "Nessun dato",
|
|
612
|
+
matrixCell: "{row} e {column}: {state}",
|
|
613
|
+
matrixTotal: "Totale",
|
|
614
|
+
matrixItemTotal: "{item}: {count}",
|
|
615
|
+
matrixExpandGroup: "Espandi {group}",
|
|
616
|
+
matrixCollapseGroup: "Comprimi {group}",
|
|
617
|
+
matrixGroupCount: "Elementi: {count}",
|
|
618
|
+
matrixDetails: "Dettagli",
|
|
619
|
+
matrixClose: "Chiudi",
|
|
620
|
+
matrixValue: "Valore",
|
|
621
|
+
matrixState: "Stato",
|
|
622
|
+
matrixAllClear: "Niente richiede attenzione",
|
|
623
|
+
matrixShowMore: "Mostra altro",
|
|
591
624
|
});
|
|
592
625
|
const staticLabels = Object.freeze({
|
|
593
626
|
overflowNote: "Righe mostrate: {shown} su {total}.",
|
|
@@ -588,6 +588,39 @@ const labels = Object.freeze({
|
|
|
588
588
|
flowCopied: "Skopiowano",
|
|
589
589
|
flowSavedIt: "Mam to zapisane",
|
|
590
590
|
flowShownOnce: "Widoczne tylko raz. Skopiuj teraz.",
|
|
591
|
+
matrixRows: "Wiersze",
|
|
592
|
+
matrixColumns: "Kolumny",
|
|
593
|
+
matrixSearch: "Szukaj wierszy i kolumn",
|
|
594
|
+
matrixSortBy: "Sortuj według",
|
|
595
|
+
matrixGroupBy: "Grupuj według",
|
|
596
|
+
matrixByLabel: "Nazwa",
|
|
597
|
+
matrixByTotal: "Razem",
|
|
598
|
+
matrixAsListed: "Jak na liście",
|
|
599
|
+
matrixNoGrouping: "Bez grup",
|
|
600
|
+
matrixDescending: "Malejąco",
|
|
601
|
+
matrixOnlyProblems: "Tylko problemy",
|
|
602
|
+
matrixHideEmpty: "Ukryj puste wiersze i kolumny",
|
|
603
|
+
matrixSize: "{rows} × {columns}",
|
|
604
|
+
matrixShown: "Wiersze: {rows} z {totalRows}, kolumny: {columns} z {totalColumns}",
|
|
605
|
+
matrixAttention: "Wymaga uwagi: {count}",
|
|
606
|
+
matrixLegend: "Stany",
|
|
607
|
+
matrixFilter: "Filtruj",
|
|
608
|
+
matrixClearFilters: "Wyczyść filtry",
|
|
609
|
+
matrixNoMatches: "Brak pasujących wierszy lub kolumn.",
|
|
610
|
+
matrixSelf: "Ten sam element",
|
|
611
|
+
matrixNoData: "Brak danych",
|
|
612
|
+
matrixCell: "{row} i {column}: {state}",
|
|
613
|
+
matrixTotal: "Razem",
|
|
614
|
+
matrixItemTotal: "{item}: {count}",
|
|
615
|
+
matrixExpandGroup: "Rozwiń {group}",
|
|
616
|
+
matrixCollapseGroup: "Zwiń {group}",
|
|
617
|
+
matrixGroupCount: "Elementy: {count}",
|
|
618
|
+
matrixDetails: "Szczegóły",
|
|
619
|
+
matrixClose: "Zamknij",
|
|
620
|
+
matrixValue: "Wartość",
|
|
621
|
+
matrixState: "Stan",
|
|
622
|
+
matrixAllClear: "Nic nie wymaga uwagi",
|
|
623
|
+
matrixShowMore: "Pokaż więcej",
|
|
591
624
|
});
|
|
592
625
|
const staticLabels = Object.freeze({
|
|
593
626
|
overflowNote: "Pokazano {shown} z {total} wierszy.",
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// Type declarations for the matrix view (docs/matrix.md): the matrix document, its state and the API of createMatrix.
|
|
2
|
+
import type { CellValue } from "./protocol.js";
|
|
3
|
+
|
|
4
|
+
/** A state a cell can hold, listed worst first: a collapsed group shows the worst state of its cells. */
|
|
5
|
+
export interface MatrixState {
|
|
6
|
+
/** A letter, then up to 63 letters, digits, hyphens and underscores. */
|
|
7
|
+
id: string;
|
|
8
|
+
label: string;
|
|
9
|
+
/** A CanopyX tone (success, warning, danger, info, muted, neutral) or a host tone the theme defines. */
|
|
10
|
+
tone?: string;
|
|
11
|
+
/** The symbol cells and the legend draw, so a state never relies on colour alone. */
|
|
12
|
+
symbol: "check" | "cross" | "warning" | "info" | "dot" | "dash" | "question" | "clock" | "pause";
|
|
13
|
+
/** Cells in this state count in the totals and stay when the reader shows only problems. */
|
|
14
|
+
attention?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** A property of an axis's items: shown in tooltips, used to group, sort and filter. */
|
|
18
|
+
export interface MatrixAttribute {
|
|
19
|
+
id: string;
|
|
20
|
+
title: string;
|
|
21
|
+
kind?: "text" | "number" | "datetime";
|
|
22
|
+
/** The filter panel lists its values with their counts. */
|
|
23
|
+
facet?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** An entity on an axis: a row or a column. */
|
|
27
|
+
export interface MatrixItem {
|
|
28
|
+
id: string;
|
|
29
|
+
label: string;
|
|
30
|
+
secondary?: string;
|
|
31
|
+
/** An http, https or mailto address, or a relative one. */
|
|
32
|
+
href?: string;
|
|
33
|
+
values?: Record<string, CellValue | { value: CellValue; text?: string }>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** One axis of the matrix. */
|
|
37
|
+
export interface MatrixAxis {
|
|
38
|
+
title?: string;
|
|
39
|
+
noun?: string;
|
|
40
|
+
attributes?: MatrixAttribute[];
|
|
41
|
+
items: MatrixItem[];
|
|
42
|
+
groupBy?: string;
|
|
43
|
+
sort?: MatrixSort;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** How an axis is sorted: by its labels, its totals or an attribute. */
|
|
47
|
+
export interface MatrixSort {
|
|
48
|
+
by: "#label" | "#total" | (string & {});
|
|
49
|
+
direction?: "asc" | "desc";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** A cell's extras: [row index, column index, value, text, title, [label, value][]]. */
|
|
53
|
+
export type MatrixNote = [number, number, number | null, (string | null)?, (string | null)?, ([string, string][] | null)?];
|
|
54
|
+
|
|
55
|
+
/** The matrix document: `{ protocol: "canopyx/2", type: "matrix" }`. */
|
|
56
|
+
export interface MatrixDocument {
|
|
57
|
+
protocol: "canopyx/2";
|
|
58
|
+
type: "matrix";
|
|
59
|
+
id: string;
|
|
60
|
+
label: string;
|
|
61
|
+
rows: MatrixAxis;
|
|
62
|
+
/** The column axis; a square matrix has none and takes its columns from its rows. */
|
|
63
|
+
columns?: MatrixAxis;
|
|
64
|
+
square?: boolean;
|
|
65
|
+
/** The column title of a square matrix. */
|
|
66
|
+
columnTitle?: string;
|
|
67
|
+
/** What the self cells of a square matrix show. */
|
|
68
|
+
diagonal?: "self" | "data";
|
|
69
|
+
states: MatrixState[];
|
|
70
|
+
/** One string per row: a state digit (0-9, a-z) per column, "." for no data. */
|
|
71
|
+
cells: string[];
|
|
72
|
+
notes?: MatrixNote[];
|
|
73
|
+
totals?: { states?: string[]; rows?: boolean; columns?: boolean };
|
|
74
|
+
attentionOnly?: boolean;
|
|
75
|
+
/** Opens hiding the rows and columns whose visible cells all lack data. */
|
|
76
|
+
hideEmpty?: boolean;
|
|
77
|
+
cellWidth?: number;
|
|
78
|
+
density?: "compact" | "normal" | "comfortable";
|
|
79
|
+
details?: "dialog" | "host";
|
|
80
|
+
phone?: "scroll" | "cards";
|
|
81
|
+
legend?: boolean;
|
|
82
|
+
/** As tall as the content when it fits, with the host's height as the bound. Default true. */
|
|
83
|
+
autoHeight?: boolean;
|
|
84
|
+
labels?: Record<string, string>;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** The reader's query: search, filters by attribute value, only problems, hide empty, and each axis's sort, groups and collapsed groups. */
|
|
88
|
+
export interface MatrixQueryState {
|
|
89
|
+
search: string;
|
|
90
|
+
attentionOnly: boolean;
|
|
91
|
+
hideEmpty: boolean;
|
|
92
|
+
filters: { rows: Record<string, string[]>; columns: Record<string, string[]> };
|
|
93
|
+
sort: { rows: MatrixSort | null; columns: MatrixSort | null };
|
|
94
|
+
group: { rows: string | null; columns: string | null };
|
|
95
|
+
collapsed: { rows: string[]; columns: string[] };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** What canopyx:matrixCell carries: the row and column (or their groups), the state and the cell's extras. */
|
|
99
|
+
export interface MatrixCellDetail {
|
|
100
|
+
matrix: string;
|
|
101
|
+
row: string | null;
|
|
102
|
+
rowGroup: string | null;
|
|
103
|
+
column: string | null;
|
|
104
|
+
columnGroup: string | null;
|
|
105
|
+
/** The state id, "self" for a self cell, or null without data. */
|
|
106
|
+
state: string | null;
|
|
107
|
+
value: number | null;
|
|
108
|
+
text: string | null;
|
|
109
|
+
title: string | null;
|
|
110
|
+
details: [string, string][];
|
|
111
|
+
/** The cells a rolled-up cell covers, and how many of them need attention. */
|
|
112
|
+
members: number;
|
|
113
|
+
attention: number;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** A state to apply: any part of the query, down to one axis of filters, sorts, groups or collapsed groups. */
|
|
117
|
+
export interface MatrixStateInput {
|
|
118
|
+
search?: string;
|
|
119
|
+
attentionOnly?: boolean;
|
|
120
|
+
hideEmpty?: boolean;
|
|
121
|
+
filters?: { rows?: Record<string, string[] | null>; columns?: Record<string, string[] | null> };
|
|
122
|
+
sort?: { rows?: MatrixSort | null; columns?: MatrixSort | null };
|
|
123
|
+
group?: { rows?: string | null; columns?: string | null };
|
|
124
|
+
collapsed?: { rows?: string[]; columns?: string[] };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Options of createMatrix and CanopyX.configure for a matrix fragment. */
|
|
128
|
+
export interface MatrixOptions {
|
|
129
|
+
view: MatrixDocument;
|
|
130
|
+
labels?: Record<string, string>;
|
|
131
|
+
state?: MatrixStateInput;
|
|
132
|
+
onReady?: (detail: { matrix: string }) => void;
|
|
133
|
+
/** Called before the details dialog; preventDefault on the event keeps it closed. */
|
|
134
|
+
onCell?: (detail: MatrixCellDetail, event: CustomEvent<MatrixCellDetail>) => void;
|
|
135
|
+
onStateChange?: (state: MatrixQueryState) => void;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** The API of a matrix. Call it once `ready` has settled; a call made before the matrix part is in waits for it and returns a promise instead. */
|
|
139
|
+
export interface MatrixApi {
|
|
140
|
+
readonly element: HTMLElement;
|
|
141
|
+
readonly view: MatrixDocument;
|
|
142
|
+
readonly ready: Promise<MatrixApi>;
|
|
143
|
+
sort(axis: "rows" | "columns", by: MatrixSort["by"] | null, direction?: "asc" | "desc"): void;
|
|
144
|
+
group(axis: "rows" | "columns", attribute: string | null): void;
|
|
145
|
+
filter(axis: "rows" | "columns", attribute: string, values: string[] | null): void;
|
|
146
|
+
setSearch(text: string): void;
|
|
147
|
+
setAttentionOnly(on: boolean): void;
|
|
148
|
+
getState(): Readonly<MatrixQueryState>;
|
|
149
|
+
setState(state: MatrixStateInput): void;
|
|
150
|
+
/** The ids of the visible items, members of collapsed groups included, in order. */
|
|
151
|
+
getVisible(): Readonly<{ rows: string[]; columns: string[] }>;
|
|
152
|
+
/** Moves focus to a cell; false when the row or column is not shown. */
|
|
153
|
+
focusCell(row: string, column: string): boolean;
|
|
154
|
+
/** Replaces the document, keeping the query that still applies (live refresh). */
|
|
155
|
+
update(view: MatrixDocument): void;
|
|
156
|
+
destroy(): void;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Creates a status matrix in a host element (docs/matrix.md). Ships with the grid and reporting entry points. */
|
|
160
|
+
export declare function createMatrix(host: HTMLElement, options: MatrixOptions): MatrixApi;
|
package/README.md
CHANGED
|
@@ -20,15 +20,16 @@ Turn application data into searchable trees, sortable tables and standalone HTML
|
|
|
20
20
|
- **Application views** backed by paged or cursor-based data sources, with selection and detail callbacks.
|
|
21
21
|
- **Email and print tables** rendered from the same .NET view without JavaScript.
|
|
22
22
|
- **Calendars and resource timelines** of the same records: month, week, day, agenda and year views, resources grouped as a tree with capacity per group, recurring events, drag to reschedule through the grid's editing, and iCalendar export.
|
|
23
|
+
- **Status matrices**: a state for each pair of two sets of entities, with totals, sorting, filtering and grouping on both axes, sticky headers and both axes windowed ([docs/matrix.md](docs/matrix.md)).
|
|
23
24
|
- **Guided flows** for setup and edit tasks: steps with typed fields, conditions as data, validation, a review page with change links, secrets that never echo, and one proposal to the host ([docs/flow.md](docs/flow.md)).
|
|
24
25
|
|
|
25
|
-

|
|
26
27
|
|
|
27
28
|
The same explorer in the dark theme, alongside its script-free output for email and print:
|
|
28
29
|
|
|
29
30
|
| Dark theme | Static outline |
|
|
30
31
|
| --- | --- |
|
|
31
|
-
|  |  |
|
|
32
33
|
|
|
33
34
|
## Features
|
|
34
35
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evotecit/canopyx",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"description": "Interactive tree grids and data tables with virtual rows, search, filters and offline HTML reports.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -48,6 +48,8 @@
|
|
|
48
48
|
"./forms.css": "./CanopyX.Html/Assets/canopyx-forms.css",
|
|
49
49
|
"./flow.js": "./CanopyX.Html/Assets/canopyx-flow.js",
|
|
50
50
|
"./flow.css": "./CanopyX.Html/Assets/canopyx-flow.css",
|
|
51
|
+
"./matrix.js": "./CanopyX.Html/Assets/canopyx-matrix.js",
|
|
52
|
+
"./matrix.css": "./CanopyX.Html/Assets/canopyx-matrix.css",
|
|
51
53
|
"./editing.js": "./CanopyX.Html/Assets/canopyx-editing.js",
|
|
52
54
|
"./editing.css": "./CanopyX.Html/Assets/canopyx-editing.css",
|
|
53
55
|
"./column-groups.js": "./CanopyX.Html/Assets/canopyx-column-groups.js",
|
|
@@ -143,6 +145,8 @@
|
|
|
143
145
|
"CanopyX.Html/Assets/canopyx-forms.css",
|
|
144
146
|
"CanopyX.Html/Assets/canopyx-flow.js",
|
|
145
147
|
"CanopyX.Html/Assets/canopyx-flow.css",
|
|
148
|
+
"CanopyX.Html/Assets/canopyx-matrix.js",
|
|
149
|
+
"CanopyX.Html/Assets/canopyx-matrix.css",
|
|
146
150
|
"CanopyX.Html/Assets/canopyx-editing.js",
|
|
147
151
|
"CanopyX.Html/Assets/canopyx-editing.css",
|
|
148
152
|
"CanopyX.Html/Assets/canopyx-column-groups.js",
|