@evotecit/canopyx 0.3.6 → 0.3.7

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.
Files changed (30) hide show
  1. package/CanopyX.Html/Assets/canopyx-calendar.js +4 -3
  2. package/CanopyX.Html/Assets/canopyx-detail-rows.js +2 -2
  3. package/CanopyX.Html/Assets/canopyx-filter-editor.js +2 -1
  4. package/CanopyX.Html/Assets/canopyx-formatting.js +7 -6
  5. package/CanopyX.Html/Assets/canopyx-grid-module.mjs +62 -40
  6. package/CanopyX.Html/Assets/canopyx-grid.css +22 -0
  7. package/CanopyX.Html/Assets/canopyx-grid.js +62 -40
  8. package/CanopyX.Html/Assets/canopyx-interactive.js +4 -4
  9. package/CanopyX.Html/Assets/canopyx-labels-de.js +3 -1
  10. package/CanopyX.Html/Assets/canopyx-labels-es.js +3 -1
  11. package/CanopyX.Html/Assets/canopyx-labels-fr.js +3 -1
  12. package/CanopyX.Html/Assets/canopyx-labels-it.js +3 -1
  13. package/CanopyX.Html/Assets/canopyx-labels-pl.js +3 -1
  14. package/CanopyX.Html/Assets/canopyx-live.js +4 -3
  15. package/CanopyX.Html/Assets/canopyx-matrix.js +3 -3
  16. package/CanopyX.Html/Assets/canopyx-module.mjs +4 -4
  17. package/CanopyX.Html/Assets/canopyx-reporting-module.mjs +64 -42
  18. package/CanopyX.Html/Assets/canopyx-reporting.css +22 -0
  19. package/CanopyX.Html/Assets/canopyx-reporting.js +64 -42
  20. package/CanopyX.Html/Assets/canopyx-tree-tools.js +7 -6
  21. package/CanopyX.Html/Assets/canopyx.js +4 -4
  22. package/JavaScript/grid.d.ts +6 -0
  23. package/JavaScript/locales/de.mjs +3 -1
  24. package/JavaScript/locales/es.mjs +3 -1
  25. package/JavaScript/locales/fr.mjs +3 -1
  26. package/JavaScript/locales/it.mjs +3 -1
  27. package/JavaScript/locales/pl.mjs +3 -1
  28. package/JavaScript/protocol.d.ts +1 -1
  29. package/README.md +6 -2
  30. package/package.json +1 -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|matrix)[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",hiddenColumns:"[one]+{count} column[other]+{count} columns",visibleColumns:"Visible columns",resizeColumn:"Resize {column}",columnWidth:"{width} pixels",loading:"Loading…",loadError:"Could not load: {error} (Enter to retry)",queryFailed:"Could not update the view: {error}",showMore:"Show more ({loaded} of {total} loaded)",showMoreUnknownTotal:"Show more ({loaded} loaded)",pageLoaded:"{loaded} of {total} loaded",itemsLoaded:"{count} items",yes:"Yes",no:"No",utcTime:"{time} UTC",localTime:"{time} local time",mountError:"This view could not be displayed: {error}"});function labelsFor(...sources){const labels=Object.assign({},DEFAULT_LABELS);for(const source of sources){if(source==null)continue;if(typeof source!=="object"||Array.isArray(source))throw new Error("CanopyX: labels must be an object of strings.");Object.keys(source).forEach((name)=>{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)}
@@ -74,7 +74,7 @@ function trusted(kind,value){if(trustPolicy===undefined){try{trustPolicy=global.
74
74
  catch(_){trustPolicy=null}}
75
75
  return trustPolicy?trustPolicy["create"+kind](value):value}
76
76
  function hostHtml(element,html,sanitize,kind){const text=String(html??""),value=sanitize?sanitize(text,{kind}):text;if(value?.nodeType)element.replaceChildren(value);else if(typeof value==="string")element.innerHTML=trusted("HTML",value);else if(global.trustedTypes?.isHTML?.(value))element.innerHTML=value;else throw new Error("CanopyX: invalid sanitizeHtml result.")}
77
- DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridViews:"Views",gridViewName:"View name",gridSaveView:"Save view",gridRemoveView:"Remove view",gridResetView:"Reset view",gridChooseView:"Choose a saved view",gridApply:"Apply",gridFindColumn:"Find a column…",gridShowAllColumns:"Show all",gridResetColumns:"Reset columns",gridReorderColumn:"{column}: drag, or press Alt+Up or Alt+Down to move",gridPinnedLeft:"Pinned left",gridPinnedRight:"Pinned right",gridResponsiveHidden:"Hidden at this width",gridExport:"Export",gridVisibleRows:"Visible rows",gridSelectedRows:"Selected rows",gridAllRows:"All rows",gridDownloadCsv:"Download CSV",gridCopy:"Copy",gridExportProgress:"{completed} of {total} records",gridExportReading:"{completed} records",gridExportCancelled:"Export cancelled",gridCancelExport:"Cancel export",gridMore:"More",gridPrint:"Print",gridDetails:"Record details",gridDetailsSection:"Details",gridPreviousRecord:"Previous record",gridNextRecord:"Next record",gridCloseRecord:"Close record details"}));DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{recordBack:"Back",recordBackTo:"Back to {record}",recordTrail:"Record trail",recordShowing:"Showing {record}",recordTabs:"Record views",recordSections:"Jump to a section",recordOtherFields:"Other fields",recordShowEmpty:"Show empty fields ({count})",recordHideEmpty:"Hide empty fields",recordActions:"Record actions",recordCopyValues:"Copy values",recordCopyJson:"Copy as JSON",recordCopyLink:"Copy link",recordCopied:"Copied to the clipboard",recordPrint:"Print record",recordExportCsv:"Export CSV",recordExportJson:"Export JSON",recordOpenAll:"Open all in grid",recordLoadMore:"Load more",recordCount:"{shown} of {total}",recordItems:"{count} items",recordMore:"+{count}",recordShowAll:"Show all {count}",recordRetry:"Retry",recordLoadFailed:"Could not load the record details: {error}",recordUnavailable:"Record {id} is not available here.",recordField:"Field",recordValue:"Value",recordSection:"Section",recordBefore:"Before",recordAfter:"After",recordAdded:"Added",recordRemoved:"Removed",recordChanged:"{count} changed fields"}));function boundedPattern(source,exact=true,whole=false){const bad=()=>{throw new Error("CanopyX: pattern requires literals, character classes and at most one variable repetition anchored at the start; groups, alternatives and backreferences are unsupported.")};if(typeof source!=="string"||source.length>256)bad();let width=0,variable=0;for(let i=0;i<source.length;){const unit=source[i++];if(unit==="^"&&i===1||unit==="$"&&i===source.length)continue;if(unit==="\\"){const escape=source[i++];if(!escape||!/^[dDsSwWbBnrtfv\\.^$*+?{}\[\]()|/]$/.test(escape)) bad();
77
+ DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{gridViews:"Views",gridViewName:"View name",gridSaveView:"Save view",gridRemoveView:"Remove view",gridResetView:"Reset view",gridChooseView:"Choose a saved view",gridApply:"Apply",gridFindColumn:"Find a column…",gridShowAllColumns:"Show all",gridResetColumns:"Reset columns",gridReorderColumn:"{column}: drag, or press Alt+Up or Alt+Down to move",gridPinnedLeft:"Pinned left",gridPinnedRight:"Pinned right",gridResponsiveHidden:"Hidden at this width",gridExport:"Export",gridVisibleRows:"Visible rows",gridSelectedRows:"Selected rows",gridAllRows:"All rows",gridDownloadCsv:"Download CSV",gridCopy:"Copy",gridExportProgress:"{completed} of {total} records",gridExportReading:"{completed} records",gridExportCancelled:"Export cancelled",gridCancelExport:"Cancel export",gridMore:"More",gridPrint:"Print",gridDetails:"Record details",gridDetailsSection:"Details",gridPreviousRecord:"Previous record",gridNextRecord:"Next record",gridCloseRecord:"Close"}));DEFAULT_LABELS=Object.freeze(Object.assign({},DEFAULT_LABELS,{recordBack:"Back",recordBackTo:"Back to {record}",recordTrail:"Record trail",recordShowing:"Showing {record}",recordTabs:"Record views",recordSections:"Jump to a section",recordOtherFields:"Other fields",recordShowEmpty:"Show empty fields ({count})",recordHideEmpty:"Hide empty fields",recordActions:"Record actions",recordCopyValues:"Copy values",recordCopyJson:"Copy as JSON",recordCopyLink:"Copy link",recordCopied:"Copied to the clipboard",recordPrint:"Print record",recordExportCsv:"Export CSV",recordExportJson:"Export JSON",recordOpenAll:"Open all in grid",recordLoadMore:"Load more",recordCount:"{shown} of {total}",recordItems:"{count} items",recordMore:"+{count}",recordShowAll:"Show all {count}",recordRetry:"Retry",recordLoadFailed:"Could not load the record details: {error}",recordUnavailable:"Record {id} is not available here.",recordField:"Field",recordValue:"Value",recordSection:"Section",recordBefore:"Before",recordAfter:"After",recordAdded:"Added",recordRemoved:"Removed",recordChanged:"{count} changed fields"}));function boundedPattern(source,exact=true,whole=false){const bad=()=>{throw new Error("CanopyX: pattern requires literals, character classes and at most one variable repetition anchored at the start; groups, alternatives and backreferences are unsupported.")};if(typeof source!=="string"||source.length>256)bad();let width=0,variable=0;for(let i=0;i<source.length;){const unit=source[i++];if(unit==="^"&&i===1||unit==="$"&&i===source.length)continue;if(unit==="\\"){const escape=source[i++];if(!escape||!/^[dDsSwWbBnrtfv\\.^$*+?{}\[\]()|/]$/.test(escape)) bad();
78
78
  }else if(unit==="["){let closed=false;while(i<source.length){const c=source[i++];if(c==="\\"){const escape=source[i++];if(!escape||!/^[dDsSwWbnrtfv\\.^$*+?{}\[\]()|/-]$/.test(escape)) bad(); }
79
79
  else if(c==="]"){closed=true;break}}
80
80
  if(!closed)bad()}else if("()|^$*+?{}]".includes(unit))bad();let count=1;if(i<source.length&&"*+?".includes(source[i])){variable++;i++}
@@ -334,7 +334,7 @@ function hostRenderers(renderers,columns,plugins){if(![Object.prototype,null].in
334
334
  Object.keys(renderers).some(id=>typeof renderers[id]!=="function"||!columns.some(column=>column.id===id)))
335
335
  throw new Error("CanopyX: invalid column renderers.");const named=Object.assign(Object.create(null),...plugins.map(item=>item.cellRenderers));return Object.assign(Object.create(null),...columns.filter(column=>typeof column.renderer==="string"&&named[column.renderer])
336
336
  .map(column=>({[column.id]:named[column.renderer]})),renderers)}
337
- function disposeHostSlot(slot){const lifetime=slot.hostLifetime;if(!lifetime)return;delete slot.hostLifetime;lifetime.dispose()}
337
+ function disposeHostSlot(slot){slot.childSlots?.forEach(disposeHostSlot);const lifetime=slot.hostLifetime;if(!lifetime)return;delete slot.hostLifetime;lifetime.dispose()}
338
338
  function disposeRowHostCells(row){row._cells?.forEach(disposeHostSlot)}
339
339
  class HostCellContext{#controller;#cleanups;constructor(slot,column,level,expanded,index){slot.hostLifetime=this;this.column=column;this.level=level;this.expanded=expanded;this.index=index}
340
340
  get signal(){this.#controller||=new AbortController();if(this.#cleanups===null)this.#controller.abort();return this.#controller.signal}
@@ -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":["1e7e68d65a0c7aeb","canopyx-tree-tools.6f6db0b476335e64.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);
507
+ const PART_URL=typeof document!=="undefined"&&document.currentScript?.src;const PARTS={"tree-tools":["4c6c4101089c4444","canopyx-tree-tools.510207e8f47014fd.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}}
@@ -354,6 +354,8 @@ interface GridDefinition<Q extends AcceptedGridQuery = GridQuery, Row extends Gr
354
354
  /** Pager settings of a paged view, read by the pager part (canopyx-pager.js); absent keeps the built-in pager. */
355
355
  pager?: GridPager;
356
356
  selection: "none" | "single" | "multiple";
357
+ /** Ordinal IDs are source positions in one snapshot; saved selection replay and keyed live updates are unavailable. Default stableKey. */
358
+ rowIdentity?: "stableKey" | "ordinal";
357
359
  search: boolean;
358
360
  /** Search-input debounce (0..5000). Absent uses 0 for inline and 300 for host sources. */
359
361
  searchDelayMs?: number;
@@ -378,6 +380,8 @@ interface GridDefinition<Q extends AcceptedGridQuery = GridQuery, Row extends Gr
378
380
  dragRows?: boolean;
379
381
  stateKey?: string;
380
382
  timeZone?: TimeZone;
383
+ /** Space-separated host styling classes copied onto body-appended record, text and column overlays when opened. */
384
+ overlayClass?: string;
381
385
  labels?: Partial<CanopyLabels>;
382
386
  /** Sanitizes trusted host HTML details before insertion; shares the tree host's sanitizer contract. */
383
387
  sanitizeHtml?: HostOptions["sanitizeHtml"];
@@ -695,6 +699,8 @@ interface GridHostBehavior<Q extends AcceptedGridQuery = GridQuery, Row extends
695
699
  /** Overrides the view's search-input debounce (0..5000); Enter always applies immediately. */
696
700
  searchDelayMs?: number;
697
701
  source?: Q extends GridCursorQuery ? GridCursorSource<Row> : GridSource<Row>;
702
+ /** Space-separated host styling classes copied onto body-appended record, text and column overlays when opened. */
703
+ overlayClass?: string;
698
704
  labels?: Partial<CanopyLabels>;
699
705
  renderers?: Record<string, (row: Row, element: HTMLElement, context: GridCellContext) => boolean | void>;
700
706
  cachePageLimit?: number;
@@ -11,6 +11,7 @@ const labels = Object.freeze({
11
11
  searchWordTooShort: "Ein Wort mit mindestens {min} Zeichen eingeben",
12
12
  searchFailed: "Suche fehlgeschlagen: {error}",
13
13
  columns: "Spalten",
14
+ hiddenColumns: "[one]+{count} Spalte[other]+{count} Spalten",
14
15
  gridFilters: "Filter",
15
16
  gridFitContent: "An Inhalt anpassen",
16
17
  gridUnpin: "Fixierung aufheben",
@@ -243,7 +244,7 @@ const labels = Object.freeze({
243
244
  gridExportCancelled: "Export abgebrochen",
244
245
  gridCancelExport: "Export abbrechen",
245
246
  gridPrint: "Drucken",
246
- gridCloseRecord: "Datensatzdetails schließen",
247
+ gridCloseRecord: "Schließen",
247
248
  gridContains: "enthält",
248
249
  gridStartsWith: "beginnt mit",
249
250
  gridIn: "ist einer der Werte",
@@ -688,6 +689,7 @@ const labels = Object.freeze({
688
689
  matrixItemTotal: "{item}: {count}",
689
690
  matrixExpandGroup: "{group} aufklappen",
690
691
  matrixCollapseGroup: "{group} zuklappen",
692
+ matrixGroupHeadingCount: "{count}",
691
693
  matrixGroupCount: "Elemente: {count}",
692
694
  matrixDetails: "Details",
693
695
  matrixClose: "Schließen",
@@ -11,6 +11,7 @@ const labels = Object.freeze({
11
11
  searchWordTooShort: "Escriba una palabra de al menos {min} caracteres",
12
12
  searchFailed: "Error al buscar: {error}",
13
13
  columns: "Columnas",
14
+ hiddenColumns: "[one]+{count} columna[other]+{count} columnas",
14
15
  gridFilters: "Filtros",
15
16
  gridFitContent: "Ajustar al contenido",
16
17
  gridUnpin: "Desfijar",
@@ -243,7 +244,7 @@ const labels = Object.freeze({
243
244
  gridExportCancelled: "Exportación cancelada",
244
245
  gridCancelExport: "Cancelar exportación",
245
246
  gridPrint: "Imprimir",
246
- gridCloseRecord: "Cerrar detalles del registro",
247
+ gridCloseRecord: "Cerrar",
247
248
  gridContains: "contiene",
248
249
  gridStartsWith: "empieza por",
249
250
  gridIn: "es uno de estos valores",
@@ -688,6 +689,7 @@ const labels = Object.freeze({
688
689
  matrixItemTotal: "{item}: {count}",
689
690
  matrixExpandGroup: "Expandir {group}",
690
691
  matrixCollapseGroup: "Contraer {group}",
692
+ matrixGroupHeadingCount: "{count}",
691
693
  matrixGroupCount: "Elementos: {count}",
692
694
  matrixDetails: "Detalles",
693
695
  matrixClose: "Cerrar",
@@ -11,6 +11,7 @@ const labels = Object.freeze({
11
11
  searchWordTooShort: "Saisissez un mot d'au moins {min} caractères",
12
12
  searchFailed: "Échec de la recherche : {error}",
13
13
  columns: "Colonnes",
14
+ hiddenColumns: "[one]+{count} colonne[other]+{count} colonnes",
14
15
  gridFilters: "Filtres",
15
16
  gridFitContent: "Ajuster au contenu",
16
17
  gridUnpin: "Désépingler",
@@ -243,7 +244,7 @@ const labels = Object.freeze({
243
244
  gridExportCancelled: "Exportation annulée",
244
245
  gridCancelExport: "Annuler l'exportation",
245
246
  gridPrint: "Imprimer",
246
- gridCloseRecord: "Fermer les détails de l'enregistrement",
247
+ gridCloseRecord: "Fermer",
247
248
  gridContains: "contient",
248
249
  gridStartsWith: "commence par",
249
250
  gridIn: "est l'une des valeurs",
@@ -688,6 +689,7 @@ const labels = Object.freeze({
688
689
  matrixItemTotal: "{item} : {count}",
689
690
  matrixExpandGroup: "Développer {group}",
690
691
  matrixCollapseGroup: "Réduire {group}",
692
+ matrixGroupHeadingCount: "{count}",
691
693
  matrixGroupCount: "Éléments : {count}",
692
694
  matrixDetails: "Détails",
693
695
  matrixClose: "Fermer",
@@ -11,6 +11,7 @@ const labels = Object.freeze({
11
11
  searchWordTooShort: "Digita una parola di almeno {min} caratteri",
12
12
  searchFailed: "Ricerca non riuscita: {error}",
13
13
  columns: "Colonne",
14
+ hiddenColumns: "[one]+{count} colonna[other]+{count} colonne",
14
15
  gridFilters: "Filtri",
15
16
  gridFitContent: "Adatta al contenuto",
16
17
  gridUnpin: "Sblocca",
@@ -243,7 +244,7 @@ const labels = Object.freeze({
243
244
  gridExportCancelled: "Esportazione annullata",
244
245
  gridCancelExport: "Annulla esportazione",
245
246
  gridPrint: "Stampa",
246
- gridCloseRecord: "Chiudi i dettagli del record",
247
+ gridCloseRecord: "Chiudi",
247
248
  gridContains: "contiene",
248
249
  gridStartsWith: "inizia con",
249
250
  gridIn: "è uno tra",
@@ -688,6 +689,7 @@ const labels = Object.freeze({
688
689
  matrixItemTotal: "{item}: {count}",
689
690
  matrixExpandGroup: "Espandi {group}",
690
691
  matrixCollapseGroup: "Comprimi {group}",
692
+ matrixGroupHeadingCount: "{count}",
691
693
  matrixGroupCount: "Elementi: {count}",
692
694
  matrixDetails: "Dettagli",
693
695
  matrixClose: "Chiudi",
@@ -11,6 +11,7 @@ const labels = Object.freeze({
11
11
  searchWordTooShort: "Wpisz słowo o długości co najmniej {min} znaków",
12
12
  searchFailed: "Nie udało się wyszukać: {error}",
13
13
  columns: "Kolumny",
14
+ hiddenColumns: "[one]+{count} kolumna[few]+{count} kolumny[many]+{count} kolumn[other]+{count} kolumny",
14
15
  gridFilters: "Filtry",
15
16
  gridFitContent: "Dopasuj do zawartości",
16
17
  gridUnpin: "Odepnij",
@@ -243,7 +244,7 @@ const labels = Object.freeze({
243
244
  gridExportCancelled: "Eksport anulowany",
244
245
  gridCancelExport: "Anuluj eksport",
245
246
  gridPrint: "Drukuj",
246
- gridCloseRecord: "Zamknij szczegóły rekordu",
247
+ gridCloseRecord: "Zamknij",
247
248
  gridContains: "zawiera",
248
249
  gridStartsWith: "zaczyna się od",
249
250
  gridIn: "jest jedną z wartości",
@@ -688,6 +689,7 @@ const labels = Object.freeze({
688
689
  matrixItemTotal: "{item}: {count}",
689
690
  matrixExpandGroup: "Rozwiń {group}",
690
691
  matrixCollapseGroup: "Zwiń {group}",
692
+ matrixGroupHeadingCount: "{count}",
691
693
  matrixGroupCount: "Elementy: {count}",
692
694
  matrixDetails: "Szczegóły",
693
695
  matrixClose: "Zamknij",
@@ -195,7 +195,7 @@ export type LabelName =
195
195
  | "gridExport" | "gridFilteredResults" | "gridSelectedRows" | "gridAllResults" | "gridVisibleRows" | "gridAllRows" | "gridDownloadCsv" | "gridCopy" | "gridDetails"
196
196
  | "searchPlaceholder" | "searchResults" | "searchResultCount" | "searchResultCountOfTotal"
197
197
  | "searchResultCountOfMany" | "searchNoResults" | "searchTooShort" | "searchWordTooShort" | "searchFailed"
198
- | "columns" | "visibleColumns" | "resizeColumn" | "columnWidth" | "loading" | "loadError" | "queryFailed"
198
+ | "columns" | "hiddenColumns" | "visibleColumns" | "resizeColumn" | "columnWidth" | "loading" | "loadError" | "queryFailed"
199
199
  | "showMore" | "showMoreUnknownTotal" | "pageLoaded" | "itemsLoaded" | "yes" | "no"
200
200
  | "utcTime" | "localTime" | "mountError" | "actions" | "editValue" | "saveEdit" | "cancelEdit"
201
201
  | "invalidEdit" | "interactionPending" | "interactionRejected" | "interactionFailed"
package/README.md CHANGED
@@ -23,13 +23,13 @@ Turn application data into searchable trees, sortable tables and standalone HTML
23
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)).
24
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)).
25
25
 
26
- ![File explorer in the light theme, with sizes, dates, owners and an expanded detail row](https://cdn.jsdelivr.net/npm/@evotecit/canopyx@0.3.6/docs/images/tree-grid-light.png)
26
+ ![File explorer in the light theme, with sizes, dates, owners and an expanded detail row](https://cdn.jsdelivr.net/npm/@evotecit/canopyx@0.3.7/docs/images/tree-grid-light.png)
27
27
 
28
28
  The same explorer in the dark theme, alongside its script-free output for email and print:
29
29
 
30
30
  | Dark theme | Static outline |
31
31
  | --- | --- |
32
- | ![File explorer in the dark theme](https://cdn.jsdelivr.net/npm/@evotecit/canopyx@0.3.6/docs/images/tree-grid-dark.png) | ![Static outline table](https://cdn.jsdelivr.net/npm/@evotecit/canopyx@0.3.6/docs/images/static-outline.png) |
32
+ | ![File explorer in the dark theme](https://cdn.jsdelivr.net/npm/@evotecit/canopyx@0.3.7/docs/images/tree-grid-dark.png) | ![Static outline table](https://cdn.jsdelivr.net/npm/@evotecit/canopyx@0.3.7/docs/images/static-outline.png) |
33
33
 
34
34
  ## Features
35
35
 
@@ -136,6 +136,10 @@ inventory.SaveHtmlPage("inventory.html");
136
136
 
137
137
  Open `inventory.html` to search the records, sort by name or size, and adjust the columns. Record IDs stay stable when the order changes.
138
138
 
139
+ Event snapshots without a key column opt in to `CanopyGridRowIdentity.Ordinal`; the typed grid assigns source positions and keeps sorting, filtering, export and drawers available. Saved selections, record deep links and keyed live updates require stable keys. Use `CanopySelectionMode.None` and leave reporting selection controls off for a read-only report with no selection consumer. See [row identity](docs/data-protocol.md#row-identity-order) and the generated `ordinal-grid.html` example.
140
+
141
+ Grid fitting reserves readable header and content minimums before preferred whitespace. Spare width restores headers first, then preferred widths, then ordinary column growth. Headers yield with a full-title tooltip only when their capped minimums cannot collectively fit; content that still needs more room scrolls horizontally. Auto height includes the actual horizontal scrollbar gutter. The [column-fitting algorithm](docs/column-fitting.md) and `report-grid-layout.html` example describe the policy.
142
+
139
143
  ### Render a table for email or print
140
144
 
141
145
  ```csharp
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evotecit/canopyx",
3
- "version": "0.3.6",
3
+ "version": "0.3.7",
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",