@evotecit/canopyx 0.3.20 → 0.3.21

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 (31) hide show
  1. package/CanopyX.Html/Assets/canopyx-grid-module.mjs +4 -5
  2. package/CanopyX.Html/Assets/canopyx-grid.js +3 -4
  3. package/CanopyX.Html/Assets/canopyx-interactive.js +3 -4
  4. package/CanopyX.Html/Assets/canopyx-labels-de.js +4 -0
  5. package/CanopyX.Html/Assets/canopyx-labels-es.js +4 -0
  6. package/CanopyX.Html/Assets/canopyx-labels-fr.js +4 -0
  7. package/CanopyX.Html/Assets/canopyx-labels-it.js +4 -0
  8. package/CanopyX.Html/Assets/canopyx-labels-pl.js +4 -0
  9. package/CanopyX.Html/Assets/canopyx-module.mjs +4 -5
  10. package/CanopyX.Html/Assets/canopyx-reporting-module.mjs +4 -5
  11. package/CanopyX.Html/Assets/canopyx-reporting.js +3 -4
  12. package/CanopyX.Html/Assets/canopyx-ui-accordion.css +119 -0
  13. package/CanopyX.Html/Assets/canopyx-ui-accordion.js +33 -0
  14. package/CanopyX.Html/Assets/canopyx-ui-module.mjs +39 -0
  15. package/CanopyX.Html/Assets/canopyx-ui-navigation.css +4 -4
  16. package/CanopyX.Html/Assets/canopyx-ui-navigation.js +5 -5
  17. package/CanopyX.Html/Assets/canopyx-ui-overlays.js +1 -1
  18. package/CanopyX.Html/Assets/canopyx-ui-stepper.js +1 -1
  19. package/CanopyX.Html/Assets/canopyx-ui-tabs.css +1 -0
  20. package/CanopyX.Html/Assets/canopyx-ui-tabs.js +7 -4
  21. package/CanopyX.Html/Assets/canopyx-ui.css +119 -0
  22. package/CanopyX.Html/Assets/canopyx-ui.js +37 -0
  23. package/CanopyX.Html/Assets/canopyx.js +3 -4
  24. package/JavaScript/locales/de.mjs +4 -0
  25. package/JavaScript/locales/es.mjs +4 -0
  26. package/JavaScript/locales/fr.mjs +4 -0
  27. package/JavaScript/locales/it.mjs +4 -0
  28. package/JavaScript/locales/pl.mjs +4 -0
  29. package/JavaScript/protocol.d.ts +1 -0
  30. package/JavaScript/ui.d.ts +70 -2
  31. package/package.json +14 -1
@@ -0,0 +1,119 @@
1
+ /* Shared semantic tokens are bundled by Sync-Assets.ps1. Only component geometry lives here. */
2
+ .cx-host,.cx-static{
3
+ --cx-compact:0;
4
+ --cx-row-h:var(--hfx-row-height,calc(40px - var(--cx-compact,0) * 8px));
5
+ --cx-size-d04em:.04em;
6
+ --cx-size-46ch:46ch;
7
+ --cx-size-440px:440px;
8
+ --cx-size-140px:140px;
9
+ --cx-size-d06em:.06em;
10
+ --cx-size-210px:210px;
11
+ --cx-size-neg-4px:-4px;
12
+ --cx-size-56px:56px;
13
+ --cx-size-64px:64px;
14
+ --cx-size-160px:160px;
15
+ --cx-size-12d5px:12.5px;
16
+ --cx-size-640px:640px;
17
+ --cx-size-17px:17px;
18
+ --cx-size-60vh:60vh;
19
+ --cx-size-11d5px:11.5px;
20
+ --cx-size-13d5px:13.5px;
21
+ --cx-size-38px:38px;
22
+ --cx-size-26px:26px;
23
+ --cx-size-52px:52px;
24
+ --cx-size-30px:30px;
25
+ --cx-size-72px:72px;
26
+ --cx-size-244px:244px;
27
+ --cx-size-44px:44px;
28
+ --cx-size-85vh:85vh;
29
+ --cx-size-480px:480px;
30
+ --cx-size-14px:14px;
31
+ --cx-size-1px:1px;
32
+ --cx-size-neg-1px:-1px;
33
+ --cx-size-8px:8px;
34
+ --cx-size-12px:12px;
35
+ --cx-size-4px:4px;
36
+ --cx-size-10px:10px;
37
+ --cx-size-11px:11px;
38
+ --cx-size-1d5px:1.5px;
39
+ --cx-size-5px:5px;
40
+ --cx-size-280px:280px;
41
+ --cx-size-34px:34px;
42
+ --cx-size-6px:6px;
43
+ --cx-size-32px:32px;
44
+ --cx-size-7px:7px;
45
+ --cx-size-420px:420px;
46
+ --cx-size-100vw:100vw;
47
+ --cx-size-16px:16px;
48
+ --cx-size-320px:320px;
49
+ --cx-size-2px:2px;
50
+ --cx-size-neg-2px:-2px;
51
+ --cx-size-180px:180px;
52
+ --cx-size-9px:9px;
53
+ --cx-size-neg-3px:-3px;
54
+ --cx-size-18px:18px;
55
+ --cx-size-1d6px:1.6px;
56
+ --cx-size-999px:999px;
57
+ --cx-size-100vh:100vh;
58
+ --cx-size-340px:340px;
59
+ --cx-size-20px:20px;
60
+ --cx-size-36px:36px;
61
+ --cx-size-3px:3px;
62
+ --cx-size-13px:13px;
63
+ --cx-size-360px:360px;
64
+ --cx-size-22px:22px;
65
+ --cx-size-24px:24px;
66
+ --cx-size-80vh:80vh;
67
+ --cx-size-240px:240px;
68
+ --cx-size-d875em:.875em;
69
+ --cx-size-92px:92px;
70
+ --cx-size-28px:28px;
71
+ --cx-scrollbar-thumb:color-mix(in srgb,var(--hfx-color-text-primary,var(--hfx-color-text,light-dark(#16181c,#f2f3f5))) 28%,transparent);
72
+ --cx-scrollbar-thumb-hover:color-mix(in srgb,var(--hfx-color-text-primary,var(--hfx-color-text,light-dark(#16181c,#f2f3f5))) 46%,transparent);
73
+ --cx-scrollbar-track:transparent;
74
+ --cx-scrollbar-size:10px;
75
+ }
76
+ .cx-host{
77
+ box-sizing:border-box;
78
+ display:flex;
79
+ flex-direction:column;
80
+ min-height:0;
81
+ color:var(--hfx-color-text-primary,var(--hfx-color-text,light-dark(#16181c,#f2f3f5)));
82
+ background:var(--hfx-color-surface-card,var(--hfx-color-surface,light-dark(#ffffff,#16181c)));
83
+ font-family:var(--hfx-font-sans,"Segoe UI Variable Text","Segoe UI",system-ui,-apple-system,"Helvetica Neue",Arial,sans-serif);
84
+ font-size:var(--hfx-control-font-size,calc(var(--hfx-type-body-size,0.875rem) - var(--cx-compact,0) * .0625rem));
85
+ line-height:var(--hfx-type-small-line,1.4);
86
+ }
87
+ .cx-host:where(:not(.cx-drawer,.cx-dialog,.cx-popover,.cx-menu,.cx-tooltip)){
88
+ height:480px;
89
+ }
90
+ .cx-host[data-cx-chrome="embedded"]{background:transparent;border:0;border-radius:0;}
91
+ .cx-host[data-cx-chrome="embedded"] > .cx-grid{border-inline:0;border-bottom:0;border-radius:0;background:transparent;}
92
+ .cx-host[data-cx-chrome="embedded"] > :is(.cx-grid-notice,.cx-grid-status,.cx-grid-pager){background:transparent;}
93
+ .cx-host[data-cx-chrome="embedded"] :is(.cx-toolbar,.cx-grid-query-summary,.cx-grid-status,.cx-grid-pager){padding-inline:var(--hfx-card-padding-x,calc(20px - var(--cx-compact,0) * 4px));}
94
+ .cx-host[data-cx-chrome="embedded"] :is(.cx-hrow,.cx-row) > :first-child{padding-inline-start:var(--hfx-card-padding-x,calc(20px - var(--cx-compact,0) * 4px));}
95
+ .cx-host[data-cx-chrome="embedded"] :is(.cx-hrow,.cx-row) > :last-child{padding-inline-end:var(--hfx-card-padding-x,calc(20px - var(--cx-compact,0) * 4px));}
96
+ .cx-host *,.cx-host *::before,.cx-host *::after{box-sizing:border-box;}
97
+ .cx-host .cx-button.cx-filter-chip{display:inline-flex;align-items:center;justify-content:flex-start;gap:var(--hfx-space-1-5,6px);min-width:0;}
98
+ .cx-filter-chip > .cx-chip-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:var(--hfx-type-small-line,1.4);}
99
+ .cx-filter-chip > .cx-chip-remove{flex:none;}
100
+ @media print{.cx-filter-chip > .cx-chip-label{white-space:normal;overflow:visible;text-overflow:clip;}}
101
+ @media not (forced-colors:active){
102
+ :where(.cx-host,.cx-host *){scrollbar-color:var(--cx-scrollbar-thumb) var(--cx-scrollbar-track);scrollbar-width:thin;}
103
+ .cx-host::-webkit-scrollbar,.cx-host ::-webkit-scrollbar{width:var(--cx-scrollbar-size);height:var(--cx-scrollbar-size);}
104
+ .cx-host::-webkit-scrollbar-track,.cx-host ::-webkit-scrollbar-track,.cx-host ::-webkit-scrollbar-corner{background:var(--cx-scrollbar-track);}
105
+ .cx-host::-webkit-scrollbar-thumb,.cx-host ::-webkit-scrollbar-thumb{background:var(--cx-scrollbar-thumb) padding-box;border:calc(var(--hfx-border-width,1px)*2) solid transparent;border-radius:var(--hfx-radius-control,var(--hfx-radius-md,6px));}
106
+ .cx-host ::-webkit-scrollbar-thumb:hover{background-color:var(--cx-scrollbar-thumb-hover);}
107
+ }
108
+ .cx-sr-only{
109
+ position:absolute !important;
110
+ width:1px;height:1px;padding:0;
111
+ margin:calc(var(--hfx-border-width,1px)*-1);
112
+ overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;
113
+ }
114
+ .cx-host[data-cx-density="compact"]{--cx-compact:1;}
115
+ @layer hfx.fallbacks{
116
+ :where([data-cx-fallback-mode="auto"],.cx-static[data-theme="auto"],.cx-static[data-cx-theme="auto"]){color-scheme:light dark;}
117
+ :where([data-cx-fallback-mode="light"],.cx-static[data-theme="light"],.cx-static[data-cx-theme="light"]){color-scheme:light;}
118
+ :where([data-cx-fallback-mode="dark"],.cx-static[data-theme="dark"],.cx-static[data-cx-theme="dark"]){color-scheme:dark;}
119
+ }
@@ -0,0 +1,37 @@
1
+ /* CanopyX UI entry: standalone UI primitives. */
2
+ (function(global){"use strict";if(global.CanopyX){return}
3
+ function h(tag,className){const element=document.createElement(tag);if(className)element.className=className;return element}
4
+ function setText(element,value){element.textContent=value==null?"":String(value)}
5
+ function fill(template,values){return template.replace(/\{(\w+)\}/g, (match, name) => (values && Object.hasOwn(values, name) ? String(values[name]) : match));
6
+ }
7
+ 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];
8
+ let rules=pluralRules.get(lang);if(!rules){try{rules=new Intl.PluralRules(lang||"en")}catch(_){rules=new Intl.PluralRules("en")}pluralRules.set(lang,rules)}
9
+ return forms[rules.select(Number(count))]??forms.other??template}
10
+ let pageNonce=typeof document!=="undefined"&&document.currentScript?.nonce||"",trustPolicy;function setNonce(nonce){if(typeof nonce!=="string"||!nonce)throw new Error("CanopyX: invalid nonce.");pageNonce=nonce}
11
+ function withNonce(element){const nonce=pageNonce||document.querySelector("script[nonce]")?.nonce;if(nonce)element.nonce=nonce;return element}
12
+ function trusted(kind,value){if(trustPolicy===undefined){try{trustPolicy=global.trustedTypes?.createPolicy("canopyx",{createHTML:text=>text,createScript:text=>text,createScriptURL:text=>text})||null}
13
+ catch(_){trustPolicy=null}}
14
+ return trustPolicy?trustPolicy["create"+kind](value):value}
15
+ 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.")}
16
+ const STATUS_FALLBACKS={"--hfx-color-status-bad-bg":"light-dark(#f9e4e4,#3c272a)","--hfx-color-status-bad-fg":"light-dark(#b8292a,#f47171)","--hfx-color-status-good-bg":"light-dark(#e2f0e9,#1f362f)","--hfx-color-status-good-fg":"light-dark(#177345,#4cc78a)","--hfx-color-status-info-bg":"light-dark(#e4ecfa,#262f41)","--hfx-color-status-info-fg":"light-dark(#2b5fc0,#74a2f7)","--hfx-color-status-neutral-bg":"light-dark(#eeeff0,#2a2c31)","--hfx-color-status-neutral-fg":"light-dark(#5b6069,#a0a5ae)","--hfx-color-status-warn-bg":"light-dark(#f8efde,#3b3322)","--hfx-color-status-warn-fg":"light-dark(#8c5c05,#eeb640)"};const MODE_ATTRIBUTES=["data-theme","data-cx-theme","data-hfx-mode","data-bs-theme","data-hfx-theme","data-hfx-tokens"];const THEME_ATTRIBUTES=[...MODE_ATTRIBUTES,"data-hfx-style","data-hfx-density","data-cx-fallback-mode","style","class"];function watchTheme(host,signal){const attributes=THEME_ATTRIBUTES;const previous=host.getAttribute("data-cx-fallback-mode");const refresh=()=>{const parent=host.parentElement&&getComputedStyle(host.parentElement);const inherited=parent?.colorScheme;const surface=parent&&["--hfx-color-surface-card","--hfx-color-surface"].some(name=>parent.getPropertyValue(name).trim());let mode=null;if(!surface){outer:for(let owner=host;owner;owner=owner.parentElement)for(const attribute of MODE_ATTRIBUTES){const value=owner.getAttribute(attribute);if(value==="light"||value==="dark"){mode=value;break outer}}}
17
+ if(surface||!mode&&inherited&&inherited!=="normal"){if(host.hasAttribute("data-cx-fallback-mode"))host.removeAttribute("data-cx-fallback-mode")}else if(host.getAttribute("data-cx-fallback-mode")!==(mode||"auto"))host.setAttribute("data-cx-fallback-mode",mode||"auto")};const observer=new MutationObserver(refresh);for(let owner=host;owner;owner=owner.parentElement)observer.observe(owner,{attributes:true,attributeFilter:attributes});refresh();const dispose=()=>{observer.disconnect();if(previous===null)host.removeAttribute("data-cx-fallback-mode");else host.setAttribute("data-cx-fallback-mode",previous)};signal?.addEventListener("abort",dispose,{once:true});return dispose}
18
+ function overlayTheme(host,element,inherit=false){for(const name of["data-hfx-style","data-hfx-mode","data-hfx-density"]){const owner=host.closest("["+name+"]");if(owner&&!inherit)element.setAttribute(name,owner.getAttribute(name));else element.removeAttribute(name)}
19
+ const theme=getComputedStyle(host),parent=inherit&&host.parentElement&&getComputedStyle(host.parentElement);element.style.colorScheme=theme.colorScheme;const copied=[];for(const name of theme)if((name.startsWith("--cx-")||name.startsWith("--hfx-"))&&(!parent||theme.getPropertyValue(name)!==parent.getPropertyValue(name))){element.style.setProperty(name,theme.getPropertyValue(name));copied.push(name)}
20
+ return copied}
21
+ function watchOverlayTheme(host,element,signal){if(signal?.aborted)return()=>{};let copied=[];const refresh=()=>{for(const name of copied)element.style.removeProperty(name);copied=overlayTheme(host,element,element.parentElement===host.parentElement)};const observer=new MutationObserver(refresh);for(let owner=host;owner;owner=owner.parentElement)observer.observe(owner,{attributes:true,attributeFilter:THEME_ATTRIBUTES});const media=["(prefers-color-scheme:dark)","(prefers-contrast:more)","(forced-colors:active)"].map(query=>matchMedia(query));for(const query of media)query.addEventListener("change",refresh);const dispose=()=>{observer.disconnect();for(const query of media)query.removeEventListener("change",refresh);signal?.removeEventListener("abort",dispose)};signal?.addEventListener("abort",dispose,{once:true});dispose.refresh=refresh;refresh();return dispose}
22
+ const PART_URL=typeof document!=="undefined"&&document.currentScript?.src;const PARTS={"ui-overlays":["24afaa117ebf3fb5","canopyx-ui-overlays.dedc42d3a501a387.js","canopyx-ui-overlays.3668a9a74004ea8e.css"],"ui-menu":["352b1ee391133ff0","canopyx-ui-menu.f9abee36db8f6ad2.js","canopyx-ui-menu.2f3478cc5f5c272f.css"],"ui-picker":["d415103478faf52a","canopyx-ui-picker.13e544bd68aca998.js","canopyx-ui-picker.59c85a1988a287a9.css"],"ui-navigation":["2553d01c5276d56a","canopyx-ui-navigation.c4ebc214197f140d.js","canopyx-ui-navigation.0afd07bbdc5aecb5.css"],"ui-tabs":["19ece8184f3daf4a","canopyx-ui-tabs.6cd35b2afd89f530.js","canopyx-ui-tabs.7bb6c6f1341e87e9.css"],"ui-stepper":["5682c49db1b85314","canopyx-ui-stepper.96973189b0f6e0c4.js","canopyx-ui-stepper.1d4a9db373b8d2f2.css"],"ui-accordion":["1424f075b78d0ccd","canopyx-ui-accordion.d0d5daa7f73d0859.js","canopyx-ui-accordion.a3ef9c27cea5b7a4.css"]},PART_CODE=new Map(),SHARED_PARTS={},partName=name=>typeof name==="string"&&/^[a-z][a-z\d-]{0,31}$/.test(name);
23
+ function registerPart(name,part){if(!partName(name)||!part||typeof part.version!=="string"||typeof part.install!=="function"||
24
+ part.requires!==undefined&&!(Array.isArray(part.requires)&&part.requires.every(partName)))throw new Error("CanopyX: invalid part.");PART_CODE.set(name,part)}
25
+ function definePart(View,name,label,context){const parts=View.prototype.parts||={};return parts[name]={name,label,context,parts}}
26
+ function loadPart(part){return part.ready||=Promise.resolve().then(async()=>{const[version,scriptFile,styleFile]=Object.hasOwn(PARTS,part.name)?PARTS[part.name]:[];part.version=version;if(!version)throw new Error("CanopyX: no "+part.label+".");if(PART_CODE.get(part.name)?.version!==part.version||part.styleReady===null){const find=type=>[...document.querySelectorAll('script[type="application/x-canopyx-'+type+'"][data-cx-version="'+part.version+'"]')].find(item=>!pageNonce||item.nonce===pageNonce);const payload=find(part.name),script=h("script"),embeddedStyle=styleFile&&find(part.name+"-style");if(payload){if(embeddedStyle){const style=h("style");style.nonce=payload.nonce;setText(style,embeddedStyle.textContent);document.head.appendChild(style)}
27
+ script.textContent=trusted("Script",payload.textContent);script.nonce=payload.nonce;document.head.appendChild(script);script.remove()}else{const base=global.CanopyXFilterEditorBase,runtime=PART_URL||typeof base==="string"&&base||document.baseURI,hashed=PART_URL&&/\.[a-f\d]{16}\.js(?:[?#]|$)/.test(PART_URL);
28
+ const load=(element,attribute,file,kind)=>new Promise((resolve,reject)=>{const url=new URL(file,runtime).href;element[attribute]=attribute==="src"?trusted("ScriptURL",url):url;element.onload=resolve;element.onerror=()=>{element.remove();reject(new Error("CanopyX: "+part.label+" "+kind+" unavailable."))};document.head.appendChild(withNonce(element))});const link=h("link");link.rel="stylesheet";const style=styleFile&&(part.styleReady||=load(link,"href",hashed?styleFile:"canopyx-"+part.name+".css","stylesheet")
29
+ .catch(error=>{part.styleReady=null;throw error}));const code=PART_CODE.get(part.name)?.version===part.version||(part.scriptReady||=load(script,"src",hashed?scriptFile:"canopyx-"+part.name+".js","script")
30
+ .finally(()=>script.remove()).catch(error=>{part.scriptReady=null;throw error}));await Promise.all([style,code])}}
31
+ const code=PART_CODE.get(part.name);if(code?.version!==part.version)throw new Error("CanopyX: "+part.label+" and runtime must come from the same build.");for(const name of code.requires||[])await loadPart(part.parts[name]||SHARED_PARTS[name]);code.install(part.context())}).catch(error=>{part.ready=null;throw error})}
32
+ const partQueue=global.CanopyXParts=global.CanopyXParts||[];partQueue.forEach(entry=>registerPart(...entry));partQueue.length=0;partQueue.push=(...entries)=>{entries.forEach(entry=>registerPart(...entry));return 0};function UiHost(){}
33
+ const UI_IMPL={},UI_TOOLS={},uiInstances=new WeakMap();const UI_KINDS={tabs:"ui-tabs",stepper:"ui-stepper",accordion:"ui-accordion",menu:"ui-menu",dialog:"ui-overlays",drawer:"ui-overlays",popover:"ui-overlays",tooltip:"ui-overlays"};const uiContext=()=>({UI_IMPL,UI_TOOLS,h,setText,fill,watchTheme,overlayTheme,hostHtml});for(const name of Object.keys(PARTS).filter(part=>part.startsWith("ui-")))SHARED_PARTS[name]=definePart(UiHost,name,name.replace("ui-","UI "),uiContext);function createUi(kind,target,options={}){const root=typeof target==="string"?document.querySelector(target):target;if(root?.nodeType!==1)throw new Error("CanopyX: "+kind+" needs an element.");let registrations=uiInstances.get(root);if(!registrations)uiInstances.set(root,registrations=new Map());if(registrations.has(kind))return registrations.get(kind);const lifetime=new AbortController();watchTheme(root,lifetime.signal);let instance=null;const ready=loadPart(SHARED_PARTS[UI_KINDS[kind]]).then(()=>{if(lifetime.signal.aborted)throw new DOMException("UI component destroyed.","AbortError");instance=UI_IMPL[kind](root,options,lifetime.signal);return instance.ready||instance});const api={element:root,ready:ready.then(()=>api),destroy(){if(lifetime.signal.aborted)return;lifetime.abort();instance?.destroy?.();registrations.delete(kind)}};for(const name of["select","getSelected","refresh","preparePrint","open","close","next","back","skip","finish","setStepState","getState","toggle","getOpen","filter"])api[name]=(...args)=>{if(lifetime.signal.aborted)throw new Error("CanopyX: UI component destroyed.");return instance?instance[name]?.(...args):ready.then(()=>instance[name]?.(...args))};api.ready.catch(error=>{if(error.name!=="AbortError")root.dispatchEvent(new CustomEvent("canopyx:error",{detail:{error},bubbles:true}))});registrations.set(kind,Object.freeze(api));return api}
34
+ function bindContextMenu(target,items,options={}){if(typeof target!=="string"&&target?.nodeType!==1)throw new Error("CanopyX: contextMenu needs an element or selector.");if(typeof target==="string")document.documentElement.matches(target);let disposed=false,cleanup=null;const dispose=()=>{disposed=true;cleanup?.()};dispose.ready=loadPart(SHARED_PARTS["ui-menu"]).then(()=>{if(!disposed)cleanup=UI_IMPL.contextMenu(target,items,options)});dispose.open=(...args)=>disposed?false:cleanup?cleanup.open(...args):dispose.ready.then(()=>!disposed&&cleanup.open(...args));dispose.close=(...args)=>cleanup?.close(...args);return dispose}
35
+ function mountUi(root=document){const mounted=[];for(const kind of Object.keys(UI_KINDS)){const selector="[data-cx-"+kind+"]",nodes=[...root.querySelectorAll(selector)];if(root.matches?.(selector))nodes.unshift(root);for(const element of nodes){if(kind==="menu"&&element.parentElement?.closest(selector))continue;if(kind==="stepper"&&["report","static"].includes(element.dataset.mode))continue;mounted.push(createUi(kind,element))}}
36
+ return mounted}
37
+ const UI_API={mountUi,contextMenu:bindContextMenu};for(const kind of Object.keys(UI_KINDS))UI_API[kind]=(target,options)=>createUi(kind,target,options);global.CanopyX=Object.freeze(Object.assign({registerPart,setNonce},UI_API));const navigation=global.performance&&performance.getEntriesByType?performance.getEntriesByType("navigation")[0]:null;if(document.readyState==="loading"||(document.readyState==="interactive"&&navigation&&navigation.domContentLoadedEventStart===0)){document.addEventListener("DOMContentLoaded",()=>mountUi(document))}else{mountUi(document)}})(typeof window!=="undefined"?window:this);
@@ -75,8 +75,7 @@ function fill(template,values){return template.replace(/\{(\w+)\}/g, (match, nam
75
75
  const pluralRules=new Map();function plural(template,count,lang){if(!template.startsWith("["))return template;const forms={};for(const match of template.matchAll(/\[(zero|one|two|few|many|other)\]([^[]*)/g)) forms[match[1]] = match[2];
76
76
  let rules=pluralRules.get(lang);if(!rules){try{rules=new Intl.PluralRules(lang||"en")}catch(_){rules=new Intl.PluralRules("en")}pluralRules.set(lang,rules)}
77
77
  return forms[rules.select(Number(count))]??forms.other??template}
78
- let DEFAULT_LABELS=Object.freeze({searchPlaceholder:"Search",searchResults:"Search results",searchResultCount:"{count} results",searchResultCountOfTotal:"{count} of {total} results",searchResultCountOfMany:"{count} of many results",searchNoResults:"No results",searchTooShort:"Type at least {min} characters",searchWordTooShort:"Type a word of at least {min} characters",searchFailed:"Search failed: {error}",columns:"Columns",hiddenColumns:"[one]+{count} column[other]+{count} columns",visibleColumns:"Visible columns",resizeColumn:"Resize {column}",columnWidth:"{width} pixels",loading:"Loading…",loadError:"Could not load: {error} (Enter to retry)",queryFailed:"Could not update the view: {error}",showMore:"Show more ({loaded} of {total} loaded)",showMoreUnknownTotal:"Show more ({loaded} loaded)",pageLoaded:"{loaded} of {total} loaded",itemsLoaded:"{count} items",yes:"Yes",no:"No",utcTime:"{time} UTC",localTime:"{time} local time",mountError:"This view could not be displayed: {error}"});function labelsFor(...sources){const labels=Object.assign({},DEFAULT_LABELS);for(const source of sources){if(source==null)continue;if(typeof source!=="object"||Array.isArray(source))throw new Error("CanopyX: labels must be an object of strings.");Object.keys(source).forEach((name)=>{const uiLabel=/^(?:tabs(?:More(?:Selected)?|ScrollPrevious|ScrollNext|Section|Position|Pause|Play|MotionDisabled)|stepper(?:StepOf|Back|Next|Finish|Skip|SaveDraft|AllSteps|Optional|Completed|Skipped|Issues?)|dialogClose|drawer(?:Close|Previous|Next)|state(?:Loading|Retry|Details))$/.test(name);
79
- if(!Object.hasOwn(DEFAULT_LABELS,name)&&!/^(?:gridEdit|format|calendar|flow|matrix|change|board|plan|path|stack|time|compare|viewIntent)[A-Z]\w{0,63}$/.test(name) && !uiLabel) throw new Error("CanopyX: there is no label '" + name + "'.");
78
+ let DEFAULT_LABELS=Object.freeze({searchPlaceholder:"Search",searchResults:"Search results",searchResultCount:"{count} results",searchResultCountOfTotal:"{count} of {total} results",searchResultCountOfMany:"{count} of many results",searchNoResults:"No results",searchTooShort:"Type at least {min} characters",searchWordTooShort:"Type a word of at least {min} characters",searchFailed:"Search failed: {error}",columns:"Columns",hiddenColumns:"[one]+{count} column[other]+{count} columns",visibleColumns:"Visible columns",resizeColumn:"Resize {column}",columnWidth:"{width} pixels",loading:"Loading…",loadError:"Could not load: {error} (Enter to retry)",queryFailed:"Could not update the view: {error}",showMore:"Show more ({loaded} of {total} loaded)",showMoreUnknownTotal:"Show more ({loaded} loaded)",pageLoaded:"{loaded} of {total} loaded",itemsLoaded:"{count} items",yes:"Yes",no:"No",utcTime:"{time} UTC",localTime:"{time} local time",mountError:"This view could not be displayed: {error}"});function labelsFor(...sources){const labels=Object.assign({},DEFAULT_LABELS);for(const source of sources){if(source==null)continue;if(typeof source!=="object"||Array.isArray(source))throw new Error("CanopyX: labels must be an object of strings.");Object.keys(source).forEach((name)=>{if(!Object.hasOwn(DEFAULT_LABELS,name)&&!/^(?:gridEdit|format|calendar|flow|matrix|change|board|plan|path|stack|time|compare|viewIntent|tabs|stepper|accordion|dialog|drawer|state)[A-Z]\w{0,63}$/.test(name)) throw new Error("CanopyX: there is no label '" + name + "'.");
80
79
  if(typeof source[name]!=="string"||!/\S/.test(source[name])) throw new Error("CanopyX: label '" + name + "' must be a string with visible text.");
81
80
  if((name==="utcTime"||name==="localTime")&&source[name].indexOf("{time}")<0)throw new Error("CanopyX: label '"+name+"' must hold {time}.");labels[name]=source[name]})}
82
81
  return Object.freeze(labels)}
@@ -531,7 +530,7 @@ close()}else{return}
531
530
  e.preventDefault()});input.addEventListener("blur",()=>{cancelNavigation();sequence++;clearTimeout(timer);clearTimeout(blurTimer);blurTimer=setTimeout(close,150)});this.cancelSearch=()=>{cancelNavigation();sequence++;clearTimeout(timer);clearTimeout(blurTimer);hits=[];close()};box.appendChild(input);box.appendChild(list);box.appendChild(status);bar.appendChild(box);this.searchInput=input}});function watchFlyout(anchorElement,panel,isOpen){const lifetime=new AbortController();let flyout=null,pending=null;const ready=()=>flyout?Promise.resolve(flyout):pending||=loadPart(SHARED_PARTS["ui-overlays"]).then(()=>{if(!lifetime.signal.aborted)flyout=UI_TOOLS.toolbarFlyout(anchorElement,panel,isOpen,lifetime.signal);return flyout}).catch(error=>{pending=null;throw error});const invoke=method=>{if(lifetime.signal.aborted||!isOpen())return;if(flyout){flyout[method]();return}
532
531
  ready().then(value=>{if(!lifetime.signal.aborted&&isOpen())value?.[method]()}).catch(error=>{if(!lifetime.signal.aborted)(anchorElement.closest(".cx-host")||anchorElement)
533
532
  .dispatchEvent(new CustomEvent("canopyx:error",{detail:{error},bubbles:true}))})};return{position:()=>invoke("position"),open:()=>invoke("open"),close:()=>flyout?.close(),dispose:()=>lifetime.abort()}}
534
- const PART_URL=typeof document!=="undefined"&&document.currentScript?.src;const PARTS={"ui-overlays":["8dc12c5deb018c2c","canopyx-ui-overlays.1e39ccf6db0a3df6.js","canopyx-ui-overlays.3668a9a74004ea8e.css"],"ui-menu":["352b1ee391133ff0","canopyx-ui-menu.f9abee36db8f6ad2.js","canopyx-ui-menu.2f3478cc5f5c272f.css"],"ui-picker":["d415103478faf52a","canopyx-ui-picker.13e544bd68aca998.js","canopyx-ui-picker.59c85a1988a287a9.css"],"ui-navigation":["d14b60f4d16a1cf3","canopyx-ui-navigation.02e11de5f457cc64.js","canopyx-ui-navigation.fb8bdcd8dba604c8.css"],"ui-tabs":["96b84956100933e7","canopyx-ui-tabs.f83f7ebb96c86512.js","canopyx-ui-tabs.bb8c7c70a8b5be2c.css"],"ui-stepper":["a31fd5d0b314cdef","canopyx-ui-stepper.dfddf053fc163c73.js","canopyx-ui-stepper.1d4a9db373b8d2f2.css"],"tree-tools":["ef0d06c82135ae2e","canopyx-tree-tools.1c4b9e286dde6065.js","canopyx-tree-tools.350dd0ee46757029.css"],"export":["15abf3205094bda6","canopyx-export.68b90138463329fe.js"],"office-export":["4fb7a4d570390ee9","canopyx-office-export.7e62c385b884e75b.js"],"links":["c6579c9401f0971d","canopyx-links.b25760079f57e6e1.js"],"search":["521eebd6e1bbbd35","canopyx-search.14c69f4551c69f23.js"],"link":["7485f5c9875e9f9b","canopyx-link.9066770aa822fe0f.js","canopyx-link.997fefec6b85f0c5.css"],"highlight":["5ebf0042011ba4fd","canopyx-highlight.ff11a0ca8e30102b.js","canopyx-highlight.9c26291cbcd0962b.css"],"layouts":["90267e35e7edf3a4","canopyx-layouts.058b8a396f909a79.js","canopyx-layouts.ca17f42babc4192a.css"],"record-panel":["cb6bcd1baf644a51","canopyx-record-panel.557c676822e8624f.js","canopyx-record-panel.174846a640de979f.css"]},PART_CODE=new Map(),SHARED_PARTS={},partName=name=>typeof name==="string"&&/^[a-z][a-z\d-]{0,31}$/.test(name);
533
+ const PART_URL=typeof document!=="undefined"&&document.currentScript?.src;const PARTS={"ui-overlays":["24afaa117ebf3fb5","canopyx-ui-overlays.dedc42d3a501a387.js","canopyx-ui-overlays.3668a9a74004ea8e.css"],"ui-menu":["352b1ee391133ff0","canopyx-ui-menu.f9abee36db8f6ad2.js","canopyx-ui-menu.2f3478cc5f5c272f.css"],"ui-picker":["d415103478faf52a","canopyx-ui-picker.13e544bd68aca998.js","canopyx-ui-picker.59c85a1988a287a9.css"],"ui-navigation":["2553d01c5276d56a","canopyx-ui-navigation.c4ebc214197f140d.js","canopyx-ui-navigation.0afd07bbdc5aecb5.css"],"ui-tabs":["19ece8184f3daf4a","canopyx-ui-tabs.6cd35b2afd89f530.js","canopyx-ui-tabs.7bb6c6f1341e87e9.css"],"ui-stepper":["5682c49db1b85314","canopyx-ui-stepper.96973189b0f6e0c4.js","canopyx-ui-stepper.1d4a9db373b8d2f2.css"],"ui-accordion":["1424f075b78d0ccd","canopyx-ui-accordion.d0d5daa7f73d0859.js","canopyx-ui-accordion.a3ef9c27cea5b7a4.css"],"tree-tools":["ef0d06c82135ae2e","canopyx-tree-tools.1c4b9e286dde6065.js","canopyx-tree-tools.350dd0ee46757029.css"],"export":["15abf3205094bda6","canopyx-export.68b90138463329fe.js"],"office-export":["4fb7a4d570390ee9","canopyx-office-export.7e62c385b884e75b.js"],"links":["c6579c9401f0971d","canopyx-links.b25760079f57e6e1.js"],"search":["521eebd6e1bbbd35","canopyx-search.14c69f4551c69f23.js"],"link":["7485f5c9875e9f9b","canopyx-link.9066770aa822fe0f.js","canopyx-link.997fefec6b85f0c5.css"],"highlight":["5ebf0042011ba4fd","canopyx-highlight.ff11a0ca8e30102b.js","canopyx-highlight.9c26291cbcd0962b.css"],"layouts":["90267e35e7edf3a4","canopyx-layouts.058b8a396f909a79.js","canopyx-layouts.ca17f42babc4192a.css"],"record-panel":["cb6bcd1baf644a51","canopyx-record-panel.557c676822e8624f.js","canopyx-record-panel.174846a640de979f.css"]},PART_CODE=new Map(),SHARED_PARTS={},partName=name=>typeof name==="string"&&/^[a-z][a-z\d-]{0,31}$/.test(name);
535
534
  function registerPart(name,part){if(!partName(name)||!part||typeof part.version!=="string"||typeof part.install!=="function"||
536
535
  part.requires!==undefined&&!(Array.isArray(part.requires)&&part.requires.every(partName)))throw new Error("CanopyX: invalid part.");PART_CODE.set(name,part)}
537
536
  function definePart(View,name,label,context){const parts=View.prototype.parts||={};return parts[name]={name,label,context,parts}}
@@ -593,7 +592,7 @@ const wanted=Array.from(new Set(ids||this.rows.filter((w)=>!w.status).map((w)=>w
593
592
  const navigation=navigationScope(this,this.dataLifetime.signal);let doc;try{doc=await navigation.wait(this.source.counts(wanted,this.sourceRequest(this.dataLifetime.signal)))}
594
593
  finally{navigation.dispose()}
595
594
  if(navigation.cancelled())return null;if(this.queryAware)snapshotRevision(doc,this.datasetRevision);checkDocument(doc,"counts");this.applyCounts(Object.assign({},doc,{items:doc.items.filter(item=>{const captured=mine.get(item.id);return captured&&this.keyMap.get(item.id)===captured.w&&captured.w.countRevision===captured.revision})}),true);return doc}});function UiHost(){}
596
- const UI_IMPL={},UI_TOOLS={},uiInstances=new WeakMap();const UI_KINDS={tabs:"ui-tabs",stepper:"ui-stepper",menu:"ui-menu",dialog:"ui-overlays",drawer:"ui-overlays",popover:"ui-overlays",tooltip:"ui-overlays"};const uiContext=()=>({UI_IMPL,UI_TOOLS,h,setText,fill,watchTheme,overlayTheme,hostHtml});for(const name of["ui-overlays","ui-menu","ui-picker","ui-navigation","ui-tabs","ui-stepper"])SHARED_PARTS[name]=definePart(UiHost,name,name.replace("ui-","UI "),uiContext);function createUi(kind,target,options={}){const root=typeof target==="string"?document.querySelector(target):target;if(root?.nodeType!==1)throw new Error("CanopyX: "+kind+" needs an element.");let registrations=uiInstances.get(root);if(!registrations)uiInstances.set(root,registrations=new Map());if(registrations.has(kind))return registrations.get(kind);const lifetime=new AbortController();watchTheme(root,lifetime.signal);let instance=null;const ready=loadPart(SHARED_PARTS[UI_KINDS[kind]]).then(()=>{if(lifetime.signal.aborted)throw new DOMException("UI component destroyed.","AbortError");instance=UI_IMPL[kind](root,options,lifetime.signal);return instance.ready||instance});const api={element:root,ready:ready.then(()=>api),destroy(){if(lifetime.signal.aborted)return;lifetime.abort();instance?.destroy?.();registrations.delete(kind)}};for(const name of["select","getSelected","refresh","preparePrint","open","close","next","back","skip","finish","setStepState","getState"])api[name]=(...args)=>{if(lifetime.signal.aborted)throw new Error("CanopyX: UI component destroyed.");return instance?instance[name]?.(...args):ready.then(()=>instance[name]?.(...args))};api.ready.catch(error=>{if(error.name!=="AbortError")root.dispatchEvent(new CustomEvent("canopyx:error",{detail:{error},bubbles:true}))});registrations.set(kind,Object.freeze(api));return api}
595
+ const UI_IMPL={},UI_TOOLS={},uiInstances=new WeakMap();const UI_KINDS={tabs:"ui-tabs",stepper:"ui-stepper",accordion:"ui-accordion",menu:"ui-menu",dialog:"ui-overlays",drawer:"ui-overlays",popover:"ui-overlays",tooltip:"ui-overlays"};const uiContext=()=>({UI_IMPL,UI_TOOLS,h,setText,fill,watchTheme,overlayTheme,hostHtml});for(const name of Object.keys(PARTS).filter(part=>part.startsWith("ui-")))SHARED_PARTS[name]=definePart(UiHost,name,name.replace("ui-","UI "),uiContext);function createUi(kind,target,options={}){const root=typeof target==="string"?document.querySelector(target):target;if(root?.nodeType!==1)throw new Error("CanopyX: "+kind+" needs an element.");let registrations=uiInstances.get(root);if(!registrations)uiInstances.set(root,registrations=new Map());if(registrations.has(kind))return registrations.get(kind);const lifetime=new AbortController();watchTheme(root,lifetime.signal);let instance=null;const ready=loadPart(SHARED_PARTS[UI_KINDS[kind]]).then(()=>{if(lifetime.signal.aborted)throw new DOMException("UI component destroyed.","AbortError");instance=UI_IMPL[kind](root,options,lifetime.signal);return instance.ready||instance});const api={element:root,ready:ready.then(()=>api),destroy(){if(lifetime.signal.aborted)return;lifetime.abort();instance?.destroy?.();registrations.delete(kind)}};for(const name of["select","getSelected","refresh","preparePrint","open","close","next","back","skip","finish","setStepState","getState","toggle","getOpen","filter"])api[name]=(...args)=>{if(lifetime.signal.aborted)throw new Error("CanopyX: UI component destroyed.");return instance?instance[name]?.(...args):ready.then(()=>instance[name]?.(...args))};api.ready.catch(error=>{if(error.name!=="AbortError")root.dispatchEvent(new CustomEvent("canopyx:error",{detail:{error},bubbles:true}))});registrations.set(kind,Object.freeze(api));return api}
597
596
  function bindContextMenu(target,items,options={}){if(typeof target!=="string"&&target?.nodeType!==1)throw new Error("CanopyX: contextMenu needs an element or selector.");if(typeof target==="string")document.documentElement.matches(target);let disposed=false,cleanup=null;const dispose=()=>{disposed=true;cleanup?.()};dispose.ready=loadPart(SHARED_PARTS["ui-menu"]).then(()=>{if(!disposed)cleanup=UI_IMPL.contextMenu(target,items,options)});dispose.open=(...args)=>disposed?false:cleanup?cleanup.open(...args):dispose.ready.then(()=>!disposed&&cleanup.open(...args));dispose.close=(...args)=>cleanup?.close(...args);return dispose}
598
597
  function mountUi(root=document){const mounted=[];for(const kind of Object.keys(UI_KINDS)){const selector="[data-cx-"+kind+"]",nodes=[...root.querySelectorAll(selector)];if(root.matches?.(selector))nodes.unshift(root);for(const element of nodes){if(kind==="menu"&&element.parentElement?.closest(selector))continue;if(kind==="stepper"&&["report","static"].includes(element.dataset.mode))continue;mounted.push(createUi(kind,element))}}
599
598
  return mounted}
@@ -914,6 +914,10 @@ const labels = Object.freeze({
914
914
  timePosition: "Momentaufnahme {index} von {count}",
915
915
  timeFrame: "Momentaufnahme um {time}",
916
916
  timeReducedMotion: "Die zeitgesteuerte Wiedergabe ist wegen reduzierter Bewegung deaktiviert. Momentaufnahmen manuell durchgehen.",
917
+ accordionSearch: "Abschnitte filtern",
918
+ accordionNoMatches: "Keine passenden Abschnitte",
919
+ accordionCopyTitle: "Titel kopieren",
920
+ accordionCopied: "Kopiert",
917
921
  tabsMore: "Mehr",
918
922
  tabsMoreSelected: "Mehr: {label}",
919
923
  tabsScrollPrevious: "Vorherige Tabs",
@@ -914,6 +914,10 @@ const labels = Object.freeze({
914
914
  timePosition: "Instantánea {index} de {count}",
915
915
  timeFrame: "Instantánea de {time}",
916
916
  timeReducedMotion: "La reproducción automática está desactivada porque el movimiento reducido está activado. Recorre las instantáneas manualmente.",
917
+ accordionSearch: "Filtrar secciones",
918
+ accordionNoMatches: "No hay secciones coincidentes",
919
+ accordionCopyTitle: "Copiar título",
920
+ accordionCopied: "Copiado",
917
921
  tabsMore: "Más",
918
922
  tabsMoreSelected: "Más: {label}",
919
923
  tabsScrollPrevious: "Pestañas anteriores",
@@ -914,6 +914,10 @@ const labels = Object.freeze({
914
914
  timePosition: "Instantané {index} sur {count}",
915
915
  timeFrame: "Instantané du {time}",
916
916
  timeReducedMotion: "La lecture automatique est désactivée car les mouvements réduits sont activés. Parcourez les instantanés manuellement.",
917
+ accordionSearch: "Filtrer les sections",
918
+ accordionNoMatches: "Aucune section correspondante",
919
+ accordionCopyTitle: "Copier le titre",
920
+ accordionCopied: "Copié",
917
921
  tabsMore: "Plus",
918
922
  tabsMoreSelected: "Plus : {label}",
919
923
  tabsScrollPrevious: "Onglets précédents",
@@ -914,6 +914,10 @@ const labels = Object.freeze({
914
914
  timePosition: "Istantanea {index} di {count}",
915
915
  timeFrame: "Istantanea del {time}",
916
916
  timeReducedMotion: "La riproduzione automatica è disattivata perché è attiva la riduzione del movimento. Scorri le istantanee manualmente.",
917
+ accordionSearch: "Filtra le sezioni",
918
+ accordionNoMatches: "Nessuna sezione corrispondente",
919
+ accordionCopyTitle: "Copia titolo",
920
+ accordionCopied: "Copiato",
917
921
  tabsMore: "Altro",
918
922
  tabsMoreSelected: "Altro: {label}",
919
923
  tabsScrollPrevious: "Schede precedenti",
@@ -914,6 +914,10 @@ const labels = Object.freeze({
914
914
  timePosition: "Stan {index} z {count}",
915
915
  timeFrame: "Stan z {time}",
916
916
  timeReducedMotion: "Automatyczne odtwarzanie jest wyłączone z powodu ograniczenia ruchu. Przeglądaj stany ręcznie.",
917
+ accordionSearch: "Filtruj sekcje",
918
+ accordionNoMatches: "Brak pasujących sekcji",
919
+ accordionCopyTitle: "Kopiuj tytuł",
920
+ accordionCopied: "Skopiowano",
917
921
  tabsMore: "Więcej",
918
922
  tabsMoreSelected: "Więcej: {label}",
919
923
  tabsScrollPrevious: "Poprzednie karty",
@@ -275,6 +275,7 @@ export type LabelName =
275
275
  | "calendarResized" | "calendarSeriesMoved" | "calendarSeriesTime" | "calendarMoveHelp" | "calendarEdit" | "calendarSaveFailed" | "calendarExport" | "calendarTimeRange" | "calendarSelected"
276
276
  | "calendarLane" | "calendarHighlighted"
277
277
  /** Standalone UI primitives; their English defaults load with the optional UI parts. */
278
+ | "accordionSearch" | "accordionNoMatches" | "accordionCopyTitle" | "accordionCopied"
278
279
  | "tabsMore" | "tabsMoreSelected" | "tabsScrollPrevious" | "tabsScrollNext" | "tabsSection" | "tabsPosition" | "tabsPause" | "tabsPlay" | "tabsMotionDisabled"
279
280
  | "stepperStepOf" | "stepperBack" | "stepperNext" | "stepperFinish" | "stepperSkip" | "stepperSaveDraft"
280
281
  | "stepperAllSteps" | "stepperOptional" | "stepperCompleted" | "stepperSkipped" | "stepperIssues" | "stepperIssue"
@@ -70,6 +70,10 @@ export interface UiTabsOptions extends UiNavigationOptions {
70
70
  transition?: "none" | "fade" | "slide";
71
71
  hash?: boolean;
72
72
  hashHistory?: "replace" | "push";
73
+ /** Remembers the selected tab in localStorage, under the root id (true) or this key. A link in the address wins. */
74
+ remember?: boolean | string;
75
+ /** Horizontal tabs share the full width equally. */
76
+ justified?: boolean;
73
77
  tabs?: readonly UiTabOptions[];
74
78
  /** Optional automatic progression. Manual selection stops it; reduced motion disables it.
75
79
  * False disables settings emitted by the .NET builder. */
@@ -94,7 +98,65 @@ export interface UiNavigationController extends UiController {
94
98
  /** Prepares deferred print bodies without navigation. Await before window.print(); rejects on load failure or cancellation. */
95
99
  preparePrint(): Promise<void>;
96
100
  }
97
- export interface UiTabsController extends UiNavigationController {}
101
+ export interface UiTabsController extends UiNavigationController {
102
+ /** Selects the next or previous enabled tab, stopping at the ends. Resolves false when there is none. */
103
+ next(): Promise<boolean>;
104
+ back(): Promise<boolean>;
105
+ }
106
+
107
+ /** An accordion section. In beforeChange, open is the state being requested. */
108
+ export interface UiAccordionItem extends UiNavigationItem {
109
+ readonly open: boolean;
110
+ }
111
+ /** Deferred settings for one section, matched by its data-accordion-id. */
112
+ export interface UiAccordionSectionOptions extends UiDeferredOptions<UiAccordionItem> {
113
+ id: string;
114
+ }
115
+ /** Accordion over the .NET heading and panel contract. Each section loads on its own; closing one aborts its pending loader. */
116
+ export interface UiAccordionOptions extends UiDeferredOptions<UiAccordionItem> {
117
+ /** Lets several sections stay open. Defaults to false: opening a section closes the open one. */
118
+ multiple?: boolean;
119
+ /** Section identities or indexes to open, instead of the markup's own. A link in the address and remembered state win over this. */
120
+ open?: readonly (string | number)[];
121
+ style?: "default" | "flush" | "separated" | "section";
122
+ indicator?: "chevron" | "plus";
123
+ indicatorPosition?: "end" | "start";
124
+ transition?: "none" | "slide";
125
+ print?: "all" | "selected";
126
+ /** Writes open sections to the address as repeated `root-id=section-id` pairs. Requires a root id. */
127
+ hash?: boolean;
128
+ hashHistory?: "replace" | "push";
129
+ /** Remembers open sections in localStorage, under the root id (true) or this key. */
130
+ remember?: boolean | string;
131
+ /** A filter box above the sections. Filtering hides sections without closing them.
132
+ * It reads the header text; `or`, `|`, quoted phrases and a leading `-` or `!` are understood. */
133
+ search?: boolean | {
134
+ placeholder?: string;
135
+ /** Delay after typing, in milliseconds. Defaults to 150. */
136
+ debounce?: number;
137
+ };
138
+ /** Adds a copy-title button to every section, or removes the ones the markup asks for. */
139
+ copyTitle?: boolean;
140
+ /** Renders an encoded icon name from the markup into a decorative node or plain text. */
141
+ renderIcon?: (icon: string, item: Partial<UiAccordionItem>) => Node | string | null | void;
142
+ labels?: UiLabels;
143
+ sections?: readonly UiAccordionSectionOptions[];
144
+ /** Returning false cancels. In a single-open accordion from is the section that would close; to.open is the requested state. */
145
+ beforeChange?: (from: UiAccordionItem | undefined, to: UiAccordionItem, signal: AbortSignal) => boolean | void | Promise<boolean | void>;
146
+ }
147
+ export interface UiAccordionController extends UiController {
148
+ open(idOrIndex: string | number): Promise<boolean>;
149
+ close(idOrIndex: string | number): Promise<boolean>;
150
+ toggle(idOrIndex: string | number): Promise<boolean>;
151
+ /** The open section identities in display order. */
152
+ getOpen(): UiResult<string[]>;
153
+ /** Filters sections by header text and returns how many remain visible. An empty query shows all. */
154
+ filter(query?: string): UiResult<number>;
155
+ /** Rediscovers the current markup and updates presentation. */
156
+ refresh(): UiResult<void>;
157
+ /** Prepares deferred print bodies without opening sections. Await before window.print(); rejects on load failure or cancellation. */
158
+ preparePrint(): Promise<void>;
159
+ }
98
160
 
99
161
  /** Step states are presentation supplied by the host or by successful navigation and validation. */
100
162
  export type UiStepState = "upcoming" | "current" | "done" | "error" | "skipped" | "disabled";
@@ -241,21 +303,27 @@ export function contextMenu(target: UiTarget,
241
303
  options?: UiContextMenuOptions): UiContextMenuDisposer;
242
304
  export function tabs(target: UiTarget, options?: UiTabsOptions): UiTabsController;
243
305
  export function stepper(target: UiTarget, options?: UiStepperOptions): UiStepperController;
306
+ export function accordion(target: UiTarget, options?: UiAccordionOptions): UiAccordionController;
244
307
  export function dialog(target: UiTarget, options?: UiDialogOptions): UiDialogController;
245
308
  export function drawer(target: UiTarget, options?: UiDrawerOptions): UiDialogController;
246
309
  export function popover(target: UiTarget, options?: UiOverlayOptions): UiOverlayController;
247
310
  export function tooltip(target: UiTarget, options?: UiOverlayOptions): UiOverlayController;
248
311
  export function menu(target: UiTarget, options?: UiMenuOptions): UiMenuController;
249
312
 
250
- /** Named UI exports shared by tree and grid/reporting entry points. */
313
+ /** Named UI exports shared by the tree, grid, reporting and UI entry points. */
251
314
  export interface UiApi {
252
315
  mountUi: typeof mountUi;
253
316
  contextMenu: typeof contextMenu;
254
317
  tabs: typeof tabs;
255
318
  stepper: typeof stepper;
319
+ accordion: typeof accordion;
256
320
  dialog: typeof dialog;
257
321
  drawer: typeof drawer;
258
322
  popover: typeof popover;
259
323
  tooltip: typeof tooltip;
260
324
  menu: typeof menu;
261
325
  }
326
+
327
+ /** The UI entry point (`@evotecit/canopyx/ui`): the UI factories, the part registration and the nonce setter. */
328
+ declare const CanopyXUi: UiApi & { registerPart(name: string, part: { version: string; requires?: string[]; install(context: object): void }): void; setNonce(nonce: string): void };
329
+ export default CanopyXUi;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evotecit/canopyx",
3
- "version": "0.3.20",
3
+ "version": "0.3.21",
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",
@@ -116,6 +116,14 @@
116
116
  "./ui-tabs.css": "./CanopyX.Html/Assets/canopyx-ui-tabs.css",
117
117
  "./ui-stepper.js": "./CanopyX.Html/Assets/canopyx-ui-stepper.js",
118
118
  "./ui-stepper.css": "./CanopyX.Html/Assets/canopyx-ui-stepper.css",
119
+ "./ui-accordion.js": "./CanopyX.Html/Assets/canopyx-ui-accordion.js",
120
+ "./ui-accordion.css": "./CanopyX.Html/Assets/canopyx-ui-accordion.css",
121
+ "./ui": {
122
+ "types": "./JavaScript/ui.d.ts",
123
+ "import": "./CanopyX.Html/Assets/canopyx-ui-module.mjs"
124
+ },
125
+ "./ui.js": "./CanopyX.Html/Assets/canopyx-ui.js",
126
+ "./ui.css": "./CanopyX.Html/Assets/canopyx-ui.css",
119
127
  "./locales/pl": {
120
128
  "types": "./JavaScript/locales/pl.d.ts",
121
129
  "import": "./JavaScript/locales/pl.mjs"
@@ -256,6 +264,11 @@
256
264
  "CanopyX.Html/Assets/canopyx-ui-tabs.css",
257
265
  "CanopyX.Html/Assets/canopyx-ui-stepper.js",
258
266
  "CanopyX.Html/Assets/canopyx-ui-stepper.css",
267
+ "CanopyX.Html/Assets/canopyx-ui-accordion.js",
268
+ "CanopyX.Html/Assets/canopyx-ui-accordion.css",
269
+ "CanopyX.Html/Assets/canopyx-ui.js",
270
+ "CanopyX.Html/Assets/canopyx-ui.css",
271
+ "CanopyX.Html/Assets/canopyx-ui-module.mjs",
259
272
  "CanopyX.Html/Assets/canopyx-board.js",
260
273
  "CanopyX.Html/Assets/canopyx-board.css",
261
274
  "CanopyX.Html/Assets/canopyx-plan.js",