@evotecit/canopyx 0.1.3 → 0.2.0
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-calendar-edit.js +113 -0
- package/CanopyX.Html/Assets/canopyx-calendar-ics.js +87 -0
- package/CanopyX.Html/Assets/canopyx-calendar-recurrence.js +36 -0
- package/CanopyX.Html/Assets/canopyx-calendar.css +279 -0
- package/CanopyX.Html/Assets/canopyx-calendar.js +307 -0
- package/CanopyX.Html/Assets/canopyx-cell-range.css +38 -0
- package/CanopyX.Html/Assets/canopyx-cell-range.js +141 -0
- package/CanopyX.Html/Assets/canopyx-column-groups.css +18 -0
- package/CanopyX.Html/Assets/canopyx-column-groups.js +51 -0
- package/CanopyX.Html/Assets/canopyx-data.js +14 -0
- package/CanopyX.Html/Assets/canopyx-detail-rows.css +18 -0
- package/CanopyX.Html/Assets/canopyx-detail-rows.js +51 -0
- package/CanopyX.Html/Assets/canopyx-editing.css +44 -0
- package/CanopyX.Html/Assets/canopyx-editing.js +134 -0
- package/CanopyX.Html/Assets/canopyx-enhance.js +126 -0
- package/CanopyX.Html/Assets/canopyx-export.js +18 -0
- package/CanopyX.Html/Assets/canopyx-facets.css +86 -0
- package/CanopyX.Html/Assets/canopyx-facets.js +65 -0
- package/CanopyX.Html/Assets/canopyx-filter-editor.css +15 -18
- package/CanopyX.Html/Assets/canopyx-filter-editor.js +23 -17
- package/CanopyX.Html/Assets/canopyx-format-editor.css +77 -0
- package/CanopyX.Html/Assets/canopyx-format-editor.js +30 -0
- package/CanopyX.Html/Assets/canopyx-formatting.css +40 -0
- package/CanopyX.Html/Assets/canopyx-formatting.js +101 -0
- package/CanopyX.Html/Assets/canopyx-grid-module.mjs +352 -246
- package/CanopyX.Html/Assets/canopyx-grid.css +139 -117
- package/CanopyX.Html/Assets/canopyx-grid.js +352 -246
- package/CanopyX.Html/Assets/canopyx-grouping.css +39 -0
- package/CanopyX.Html/Assets/canopyx-grouping.js +85 -0
- package/CanopyX.Html/Assets/canopyx-highlight.css +34 -0
- package/CanopyX.Html/Assets/canopyx-highlight.js +80 -0
- package/CanopyX.Html/Assets/canopyx-history.js +13 -0
- package/CanopyX.Html/Assets/canopyx-interactive.css +18 -6
- package/CanopyX.Html/Assets/canopyx-interactive.js +179 -100
- package/CanopyX.Html/Assets/canopyx-labels-de.js +304 -0
- package/CanopyX.Html/Assets/canopyx-labels-es.js +304 -0
- package/CanopyX.Html/Assets/canopyx-labels-fr.js +529 -0
- package/CanopyX.Html/Assets/canopyx-labels-it.js +529 -0
- package/CanopyX.Html/Assets/canopyx-labels-pl.js +304 -0
- package/CanopyX.Html/Assets/canopyx-layouts.css +48 -0
- package/CanopyX.Html/Assets/canopyx-layouts.js +39 -0
- package/CanopyX.Html/Assets/canopyx-link.css +19 -0
- package/CanopyX.Html/Assets/canopyx-link.js +38 -0
- package/CanopyX.Html/Assets/canopyx-links.js +14 -0
- package/CanopyX.Html/Assets/canopyx-live.css +24 -0
- package/CanopyX.Html/Assets/canopyx-live.js +88 -0
- package/CanopyX.Html/Assets/canopyx-module.mjs +180 -100
- package/CanopyX.Html/Assets/canopyx-pager.css +10 -0
- package/CanopyX.Html/Assets/canopyx-pager.js +14 -0
- package/CanopyX.Html/Assets/canopyx-record-panel.css +111 -0
- package/CanopyX.Html/Assets/canopyx-record-panel.js +178 -0
- package/CanopyX.Html/Assets/canopyx-reporting-module.mjs +406 -342
- package/CanopyX.Html/Assets/canopyx-reporting.css +140 -135
- package/CanopyX.Html/Assets/canopyx-reporting.js +406 -342
- package/CanopyX.Html/Assets/canopyx-search.js +32 -0
- package/CanopyX.Html/Assets/canopyx-timeline.css +80 -0
- package/CanopyX.Html/Assets/canopyx-timeline.js +88 -0
- package/CanopyX.Html/Assets/canopyx-tree-tools.css +103 -0
- package/CanopyX.Html/Assets/canopyx-tree-tools.js +92 -0
- package/CanopyX.Html/Assets/canopyx.css +17 -5
- package/CanopyX.Html/Assets/canopyx.js +175 -96
- package/JavaScript/calendar.d.ts +362 -0
- package/JavaScript/cell-ranges.d.ts +76 -0
- package/JavaScript/column-groups.d.ts +32 -0
- package/JavaScript/editing.d.ts +257 -0
- package/JavaScript/enhance.d.ts +202 -0
- package/JavaScript/facets.d.ts +58 -0
- package/JavaScript/formatting.d.ts +88 -0
- package/JavaScript/grid.d.ts +409 -56
- package/JavaScript/hosts.d.ts +177 -2
- package/JavaScript/index.d.ts +53 -4
- package/JavaScript/live.d.ts +115 -0
- package/JavaScript/locales/de.mjs +304 -0
- package/JavaScript/locales/es.mjs +304 -0
- package/JavaScript/locales/fr.d.ts +6 -0
- package/JavaScript/locales/fr.mjs +527 -0
- package/JavaScript/locales/it.d.ts +6 -0
- package/JavaScript/locales/it.mjs +527 -0
- package/JavaScript/locales/pl.mjs +304 -0
- package/JavaScript/plugins.d.ts +113 -0
- package/JavaScript/protocol.d.ts +139 -6
- package/JavaScript/records.d.ts +136 -0
- package/README.md +61 -9
- package/README.nuget.md +1 -1
- package/package.json +99 -3
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
.cx-format-editor{width:calc(100% - var(--cx-size-32px));max-width:var(--cx-size-720px,720px);max-height:var(--cx-size-85vh);padding:0;border:var(--cx-size-1px) solid var(--cx-line);border-radius:var(--cx-radius);background:var(--cx-surface);box-shadow:var(--cx-panel-shadow);flex-direction:column;overflow:hidden;}
|
|
2
|
+
.cx-format-editor[open]{display:flex;}
|
|
3
|
+
body .cx-host.cx-grid-overlay.cx-format-editor{height:fit-content;}
|
|
4
|
+
.cx-format-editor::backdrop{background:color-mix(in srgb,var(--cx-shadow-ink) 35%,transparent);}
|
|
5
|
+
.cx-format-editor .cx-drawer-h{display:flex;align-items:center;gap:var(--cx-size-12px);padding:var(--cx-size-12px) var(--cx-size-16px);border-bottom:var(--cx-size-1px) solid var(--cx-line);}
|
|
6
|
+
.cx-format-editor .cx-drawer-h h4{margin:0;flex:1;font-size:var(--cx-size-17px);}
|
|
7
|
+
.cx-format-body{flex:0 1 auto;min-height:0;overflow:auto;padding:var(--cx-size-14px) var(--cx-size-16px);}
|
|
8
|
+
.cx-format-editor > .cx-grid-control-error{margin:0;padding:0 var(--cx-size-16px) var(--cx-size-8px);}
|
|
9
|
+
.cx-format-editor > .cx-pop-f{gap:var(--cx-size-12px);flex-wrap:wrap;}
|
|
10
|
+
.cx-format-editor .cx-pop-f > .cx-format-legend-switch{margin-right:auto;font-size:var(--cx-size-13px);}
|
|
11
|
+
.cx-format-editor .cx-pop-f > .cx-link{border:0;background:transparent;color:var(--cx-accent);font-weight:600;}
|
|
12
|
+
.cx-format-editor .cx-muted{color:var(--cx-muted);}
|
|
13
|
+
.cx-format-rules{list-style:none;margin:0;padding:0;display:grid;gap:var(--cx-size-2px);}
|
|
14
|
+
.cx-format-item{display:grid;grid-template-columns:auto auto minmax(0,1fr) auto;align-items:center;gap:var(--cx-size-10px);padding:var(--cx-size-6px) var(--cx-size-4px);border-bottom:var(--cx-size-1px) solid var(--cx-line);}
|
|
15
|
+
.cx-format-item input[type="checkbox"]{width:var(--cx-size-16px);height:var(--cx-size-16px);margin:0;accent-color:var(--cx-accent);}
|
|
16
|
+
.cx-format-item-text{display:grid;min-width:0;}
|
|
17
|
+
.cx-format-item-text > *{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
|
18
|
+
.cx-format-item-text .cx-muted{font-size:var(--cx-size-12d5px);}
|
|
19
|
+
.cx-format-actions{display:flex;gap:var(--cx-size-4px);}
|
|
20
|
+
.cx-format-editor .cx-button.icon{width:var(--cx-control-h);min-width:var(--cx-control-h);padding:0;display:inline-grid;place-items:center;}
|
|
21
|
+
.cx-format-editor .cx-button:disabled{opacity:.45;cursor:default;}
|
|
22
|
+
.cx-format-editor .cx-format-swatch{position:relative;isolation:isolate;display:inline-grid;place-items:center;min-width:var(--cx-size-24px);height:var(--cx-size-20px);border-radius:var(--cx-radius-sm);border:var(--cx-size-1px) solid var(--cx-line);font-size:var(--cx-size-12px);color:var(--cx-fmt,var(--cx-text-2));}
|
|
23
|
+
.cx-format-editor .cx-format-swatch.cx-fmt-fill{background:color-mix(in srgb,var(--cx-fmt) 13%,var(--cx-surface));border-color:color-mix(in srgb,var(--cx-fmt) 35%,var(--cx-surface));}
|
|
24
|
+
.cx-format-editor .cx-format-swatch.cx-fmt-gradient{width:var(--cx-size-36px);background:var(--cx-fmt-scale);opacity:.6;}
|
|
25
|
+
.cx-format-editor .cx-format-swatch.cx-fmt-barred{width:var(--cx-size-36px);padding:0 var(--cx-size-3px);}
|
|
26
|
+
.cx-format-editor .cx-format-swatch .cx-fmt-bar{width:100%;max-width:none;}
|
|
27
|
+
.cx-format-form.cx-fb{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--cx-size-12px) var(--cx-size-16px);max-width:none;}
|
|
28
|
+
.cx-format-editor .cx-format-field{display:grid;align-items:start;gap:var(--cx-size-4px);min-width:0;font-size:var(--cx-size-13px);color:var(--cx-text-2);}
|
|
29
|
+
.cx-format-field > .cx-input{color:var(--cx-text);font-size:var(--cx-size-14px);}
|
|
30
|
+
.cx-format-form > :is(.cx-format-kinds,.cx-format-columns,.cx-format-when,.cx-format-conditions,.cx-format-style){grid-column:1/-1;}
|
|
31
|
+
.cx-format-kind{justify-self:start;max-width:100%;flex-wrap:wrap;}
|
|
32
|
+
.cx-format-editor .cx-fb .cx-format-kind button{text-transform:none;}
|
|
33
|
+
.cx-format-editor h4:focus{outline:none;}
|
|
34
|
+
.cx-format-columns{display:flex;flex-wrap:wrap;gap:var(--cx-size-6px) var(--cx-size-16px);margin:0;padding:var(--cx-size-8px) var(--cx-size-10px);border:var(--cx-size-1px) solid var(--cx-line);border-radius:var(--cx-radius);font-size:var(--cx-size-13px);}
|
|
35
|
+
.cx-format-columns legend{padding:0 var(--cx-size-4px);color:var(--cx-text-2);}
|
|
36
|
+
.cx-format-editor .cx-format-when{display:flex;align-items:center;gap:var(--cx-size-8px);font-weight:600;}
|
|
37
|
+
.cx-format-conditions{display:grid;gap:var(--cx-size-8px);padding:var(--cx-size-10px);border:var(--cx-size-1px) solid var(--cx-line);border-radius:var(--cx-radius);}
|
|
38
|
+
.cx-format-style{display:grid;grid-template-columns:repeat(auto-fit,minmax(var(--cx-size-140px),1fr));gap:var(--cx-size-10px) var(--cx-size-12px);align-items:end;}
|
|
39
|
+
.cx-format-bands{grid-column:1/-1;display:grid;gap:var(--cx-size-8px);}
|
|
40
|
+
.cx-format-band{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr) minmax(0,1.4fr) var(--cx-control-h);gap:var(--cx-size-8px);align-items:end;}
|
|
41
|
+
.cx-format-above{align-self:center;font-size:var(--cx-size-13px);color:var(--cx-text-2);}
|
|
42
|
+
.cx-format-style > .cx-link{justify-self:start;border:0;background:transparent;color:var(--cx-accent);font-weight:600;}
|
|
43
|
+
.cx-format-editor .cx-fb .cx-grid-filter.cx-cond{grid-template-columns:var(--cx-size-56px) minmax(0,1.2fr) minmax(0,1fr) minmax(0,1.2fr) auto var(--cx-size-32px);}
|
|
44
|
+
.cx-format-editor .cx-fb .cx-grid-filter .cx-condition-column{grid-column:2;grid-row:1;}
|
|
45
|
+
.cx-format-editor .cx-fb .cx-grid-filter .cx-condition-operator{grid-column:3;grid-row:1;}
|
|
46
|
+
.cx-format-editor .cx-fb .cx-grid-filter .cx-condition-value{grid-column:4;grid-row:1;}
|
|
47
|
+
.cx-format-editor .cx-fb .cx-grid-filter .cx-condition-upper{grid-column:4;grid-row:2;}
|
|
48
|
+
.cx-format-editor .cx-fb .cx-grid-filter .cx-condition-type{grid-column:2;grid-row:2;}
|
|
49
|
+
.cx-format-editor .cx-fb .cx-grid-filter > .cx-format-case{grid-column:5;grid-row:1;width:var(--cx-control-h);padding:0;font-weight:700;}
|
|
50
|
+
.cx-format-editor .cx-fb .cx-grid-filter > .cx-format-case[aria-pressed="true"]{border-color:var(--cx-accent);background:var(--cx-accent-soft);color:var(--cx-accent-text);}
|
|
51
|
+
.cx-format-editor .cx-fb .cx-grid-filter > button.icon{grid-column:6;grid-row:1;}
|
|
52
|
+
.cx-format-editor .cx-fb .cx-adds .cx-link{border:0;background:transparent;color:var(--cx-accent);font-weight:600;padding:0;}
|
|
53
|
+
.cx-format-editor .cx-fb .cx-cond .w{line-height:var(--cx-control-h);}
|
|
54
|
+
body .cx-host.cx-format-editor.cx-phone{inset:0;width:100%;max-width:none;height:100%;max-height:none;margin:0;border-radius:0;}
|
|
55
|
+
.cx-format-editor.cx-phone .cx-format-form.cx-fb{grid-template-columns:minmax(0,1fr);}
|
|
56
|
+
.cx-format-editor.cx-phone .cx-format-kind{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));justify-self:stretch;}
|
|
57
|
+
.cx-format-editor.cx-phone .cx-fb .cx-format-kind button:nth-child(3){border-left:0;}
|
|
58
|
+
.cx-format-editor.cx-phone .cx-fb .cx-format-kind button:nth-child(n+3){border-top:var(--cx-size-1px) solid var(--cx-line-strong);}
|
|
59
|
+
.cx-format-editor.cx-phone :is(.cx-input,.cx-button,select,input:not([type="checkbox"])){min-height:var(--cx-size-44px);}
|
|
60
|
+
.cx-format-editor.cx-phone .cx-button.icon{width:var(--cx-size-44px);min-width:var(--cx-size-44px);}
|
|
61
|
+
.cx-format-editor.cx-phone .cx-format-item{grid-template-columns:auto auto minmax(0,1fr);row-gap:var(--cx-size-6px);}
|
|
62
|
+
.cx-format-editor.cx-phone .cx-format-actions{grid-column:1/-1;justify-content:flex-end;}
|
|
63
|
+
.cx-format-editor.cx-phone .cx-format-band{grid-template-columns:minmax(0,1fr) minmax(0,1fr) var(--cx-size-44px);}
|
|
64
|
+
.cx-format-editor.cx-phone .cx-format-band > .cx-format-field:nth-last-child(2){grid-column:1/3;}
|
|
65
|
+
.cx-format-editor.cx-phone .cx-fb .cx-grid-filter.cx-cond{grid-template-columns:minmax(0,1fr) minmax(0,1fr) auto var(--cx-size-44px)!important;}
|
|
66
|
+
.cx-format-editor.cx-phone .cx-fb .cx-cond .w{grid-column:1/-1;grid-row:1;text-align:left;}
|
|
67
|
+
.cx-format-editor.cx-phone .cx-fb .cx-grid-filter .cx-condition-column{grid-column:1;grid-row:2;}
|
|
68
|
+
.cx-format-editor.cx-phone .cx-fb .cx-grid-filter .cx-condition-operator{grid-column:2/4;grid-row:2;}
|
|
69
|
+
.cx-format-editor.cx-phone .cx-fb .cx-grid-filter .cx-condition-value{grid-column:1/3;grid-row:3;}
|
|
70
|
+
.cx-format-editor.cx-phone .cx-fb .cx-grid-filter .cx-condition-upper{grid-column:1/3;grid-row:4;}
|
|
71
|
+
.cx-format-editor.cx-phone .cx-fb .cx-grid-filter .cx-condition-type{grid-column:1/3;grid-row:5;}
|
|
72
|
+
.cx-format-editor.cx-phone .cx-fb .cx-grid-filter > .cx-format-case{grid-column:3;grid-row:3;width:var(--cx-size-44px);}
|
|
73
|
+
.cx-format-editor.cx-phone .cx-fb .cx-grid-filter > button.icon{grid-column:4;grid-row:2;}
|
|
74
|
+
.cx-format-editor.cx-phone .cx-adds{padding-left:0;}
|
|
75
|
+
.cx-format-editor .cx-group-h{flex-wrap:wrap;}
|
|
76
|
+
.cx-format-editor .cx-group-h > .cx-seg{flex:none;}
|
|
77
|
+
.cx-format-editor.cx-phone .cx-grid-filter-group{margin-left:var(--cx-size-4px);}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
(function(global){"use strict";const installed=new WeakSet();const part={version:"24f176a39c02944a",requires:["filter-editor","formatting"],install(context){if(installed.has(context.DataGrid.prototype))return;const{DataGrid,h,setText,fill,gridButton,gridOption,gridDateInputMoment,gridInputLabel,gridIcon,gridDecorateButton,announceMenu,formatCompile,formatToneVar,formatError,FORMAT_ICONS,FORMAT_TONE_ICONS,FORMAT_OPERATORS,svgIcon}=context;const FORMAT_EDITOR_LABELS={formatRule:"Rule {number}",formatAddRule:"Add rule",formatEditRule:"Edit {rule}",formatDeleteRule:"Delete {rule}",formatMoveUp:"Move {rule} up",formatMoveDown:"Move {rule} down",formatEnableRule:"Apply {rule}",formatNoRules:"No formatting rules.",formatName:"Name",formatDescription:"Screen reader description",formatOnlyRowsWhere:"Only rows where",formatEveryRow:"Every row",formatNone:"None",formatApplyTo:"Apply to",formatTargetCell:"Cell",formatTargetRow:"Whole row",formatTargetColumns:"Chosen columns",formatStyle:"Style",formatHighlight:"Highlight",formatDataBar:"Data bar",formatColorScale:"Colour scale",formatBands:"Threshold bands",formatTone:"Tone",formatEmphasis:"Emphasis",formatFill:"Fill",formatTextOnly:"Text colour",formatStrong:"Bold fill",formatIcon:"Icon",formatNoIcon:"No icon",formatAutomatic:"Automatic",formatBadge:"Badge",formatCue:"Text cue",formatLowest:"Lowest",formatMidpoint:"Midpoint",formatHighest:"Highest",formatAddBand:"+ Band",formatRemoveBand:"Remove band",formatSave:"Save rule",formatCancel:"Cancel",formatShowLegend:"Show legend",formatInvalid:"Complete the rule before saving.",formatMatchCase:"Match case",formatToneDanger:"Danger",formatToneWarning:"Warning",formatToneSuccess:"Success",formatToneInfo:"Info",formatToneNeutral:"Neutral",formatToneMuted:"Muted",formatNotContains:"does not contain",formatEndsWith:"ends with",formatNotIn:"is none of",formatBetween:"between",formatLike:"matches wildcard",formatMatches:"matches pattern",formatOlderThan:"older than (days)",formatWithinLast:"within the last (days)",formatNotLike:"does not match wildcard",formatNotMatches:"does not match pattern",formatBetweenExclusive:"between (exclusive)"};function formatEditorTheme(grid,dialog){dialog.setAttribute("data-cx-theme",grid.host.getAttribute("data-cx-theme")||"auto");const theme=getComputedStyle(grid.host);dialog.style.colorScheme=theme.colorScheme;for(const name of theme)if(name.startsWith("--cx-"))dialog.style.setProperty(name,theme.getPropertyValue(name))}
|
|
2
|
+
function formatIconButton(label,icon,run){const button=gridDecorateButton(gridButton("",run),icon==="edit"?"more":icon,true);if(icon==="edit")button.querySelector("svg").replaceWith(svgIcon('<path d="M4 20h4L19 9l-4-4L4 16zM13.5 6.5l4 4"/>'));button.classList.add("icon");button.setAttribute("aria-label",label);button.title=label;return button}
|
|
3
|
+
function formatPreview(style){const swatch=h("span","cx-format-swatch"),stops=style.scale?style.scale.stops.map(stop=>stop.tone):style.bands?style.bands.map(band=>band.tone):null;swatch.setAttribute("aria-hidden","true");if(stops){swatch.classList.add("cx-fmt-gradient");swatch.style.setProperty("--cx-fmt-scale","linear-gradient(90deg,"+stops.map(formatToneVar).join(",")+ ")")}
|
|
4
|
+
else if(style.bar){swatch.classList.add("cx-fmt-barred");const bar=h("span","cx-fmt-bar");bar.style.setProperty("--cx-fmt-w","70%");bar.style.setProperty("--cx-fmt",formatToneVar(style.bar.tone||"info"));swatch.appendChild(bar)}
|
|
5
|
+
else if(style.tone){swatch.classList.add("cx-fmt-fill");swatch.style.setProperty("--cx-fmt",formatToneVar(style.tone));const name=!style.icon||style.icon==="auto"?FORMAT_TONE_ICONS[style.tone]||"dot":style.icon;if(name!=="none")setText(swatch,FORMAT_ICONS[name]||name)}else if(style.badge)setText(swatch,style.badge.slice(0,2));return swatch}
|
|
6
|
+
Object.assign(DataGrid.prototype,{showFormatEditor(){this.formatEditor?.close();if(!this.formatLabels.formatAddRule){const own={};for(const name of Object.keys(FORMAT_EDITOR_LABELS))own[name]=Object.hasOwn(this.labels,name)?this.labels[name]:FORMAT_EDITOR_LABELS[name];this.formatLabels={...this.formatLabels,...own}}
|
|
7
|
+
const labels=this.formatLabels,returnFocus=document.activeElement,phone=this.host.clientWidth<=640;const dialog=h("dialog","cx-host cx-grid-overlay cx-format-editor"),head=h("div","cx-drawer-h"),title=h("h4"),body=h("div","cx-format-body"),status=h("p","cx-grid-control-error"),foot=h("div","cx-pop-f");formatEditorTheme(this,dialog);dialog.classList.toggle("cx-phone",phone);title.id=this.id+"-format-title";title.tabIndex=-1;setText(title,labels.formatRules);dialog.setAttribute("aria-labelledby",title.id);status.setAttribute("role","status");head.append(title,formatIconButton(labels.formatCancel,"close",()=>dialog.close()));dialog.append(head,body,status,foot);document.body.appendChild(dialog);this.formatEditor=dialog;const editor={dialog,body,foot,status,title,rules:JSON.parse(JSON.stringify(this.getFormatRules()))};editor.apply=()=>this.setFormatRules(editor.rules).then(()=>{setText(status,"");return true},error=>{setText(status,error.message);return false});editor.list=focus=>this.renderFormatList(editor,focus);dialog.addEventListener("keydown",event=>{if(event.key==="Escape"){event.preventDefault();event.stopPropagation();if(editor.back)editor.back();else dialog.close()}});dialog.addEventListener("close",()=>{dialog.remove();if(this.formatEditor===dialog)this.formatEditor=null;if(!this.destroyed)(returnFocus?.isConnected&&returnFocus.checkVisibility?.()?returnFocus:this.grid).focus({preventScroll:true})},{once:true});const owner=new AbortController();this.lifetime.signal.addEventListener("abort",()=>dialog.close(),{once:true,signal:owner.signal});dialog.addEventListener("close",()=>owner.abort(),{once:true});announceMenu(dialog);dialog.showModal();editor.list();title.focus({preventScroll:true});return dialog},renderFormatList(editor,focus){const labels=this.formatLabels,{body,foot,rules}=editor,name=(rule,index)=>rule.label||fill(labels.formatRule,{number:index+1});editor.back=null;setText(editor.title,labels.formatRules);body.replaceChildren();foot.replaceChildren();const list=h("ol","cx-format-rules");list.setAttribute("aria-label",labels.formatRules);const change=(mutate,next)=>{mutate();editor.apply().then(ok=>{if(!ok)editor.rules=JSON.parse(JSON.stringify(this.getFormatRules()));editor.list(next)})};rules.forEach((rule,index)=>{const item=h("li","cx-format-item"),toggle=h("input"),text=h("span","cx-format-item-text"),title=h("b"),summary=h("span","cx-muted"),actions=h("span","cx-format-actions");const label=name(rule,index);toggle.type="checkbox";toggle.checked=rule.enabled!==false;toggle.setAttribute("aria-label",fill(labels.formatEnableRule,{rule:label}));toggle.addEventListener("change",()=>change(()=>{if(toggle.checked)delete rule.enabled;else rule.enabled=false},"toggle:"+index));setText(title,label);setText(summary,this.formatSummary(rule));text.append(title,summary);const move=(by,key)=>()=>change(()=>rules.splice(index+by,0,rules.splice(index,1)[0]),key+":" +(index+by));const up=formatIconButton(fill(labels.formatMoveUp,{rule:label}),"up",move(-1,"up")),down=formatIconButton(fill(labels.formatMoveDown,{rule:label}),"down",move(1,"down"));up.disabled=index===0;down.disabled=index===rules.length-1;const edit=formatIconButton(fill(labels.formatEditRule,{rule:label}),"edit",()=>this.renderFormatRule(editor,index));edit.dataset.cxFocus="edit:"+index;const remove=formatIconButton(fill(labels.formatDeleteRule,{rule:label}),"close",()=>change(()=>rules.splice(index,1),"add"));toggle.dataset.cxFocus="toggle:"+index;up.dataset.cxFocus="up:"+index;down.dataset.cxFocus="down:"+index;actions.append(up,down,edit,remove);item.append(toggle,formatPreview(rule.style||{}),text,actions);list.appendChild(item)});if(rules.length)body.appendChild(list);else{const empty=h("p","cx-muted");setText(empty,labels.formatNoRules);body.appendChild(empty)}
|
|
8
|
+
const add=gridButton(labels.formatAddRule,()=>this.renderFormatRule(editor,-1));add.classList.add("cx-link");add.dataset.cxFocus="add";add.disabled=rules.length>=64;const legend=h("input"),legendLabel=gridInputLabel(labels.formatShowLegend,legend);legend.type="checkbox";legend.checked=!!this.formatLegendShown;legendLabel.classList.add("cx-format-legend-switch");legend.addEventListener("change",()=>this.setFormatLegend(legend.checked));const done=gridButton(this.labels.gridDone,()=>editor.dialog.close());foot.append(add,legendLabel,done);const target=focus&&editor.dialog.querySelector('[data-cx-focus="'+focus+'"]');if(target&&!target.disabled)target.focus({preventScroll:true});else if(focus)add.focus({preventScroll:true})},formatSummary(rule){const labels=this.formatLabels,title=id=>this.columns.find(column=>column.id===id)?.title||id,style=rule.style||{};const target=rule.target==="row"?labels.formatTargetRow:rule.target==="columns"?(rule.columns||[]).map(title).join(", "):labels.formatTargetCell+" · "+title(rule.column);const kind=style.bar?labels.formatDataBar:style.scale?labels.formatColorScale:style.bands?labels.formatBands:labels.formatHighlight;return[target,kind,rule.when?null:labels.formatEveryRow].filter(Boolean).join(" · ")}});const FORMAT_TONES=["danger","warning","success","info","neutral","muted"];function formatField(text,control,className){const label=control.tagName==="DIV"?h("div"):gridInputLabel(text,control);if(control.tagName==="DIV"){const heading=h("span");setText(heading,text);heading.setAttribute("aria-hidden","true");label.append(heading,control)}
|
|
9
|
+
label.classList.add("cx-format-field");if(className)label.classList.add(className);if(control.tagName==="SELECT"||control.tagName==="INPUT"&&control.type!=="checkbox")control.classList.add("cx-input");return label}
|
|
10
|
+
function formatSelect(options,value){const select=h("select");for(const[key,text]of options)select.appendChild(gridOption(key,text));if(value!==undefined&&options.some(option=>option[0]===value))select.value=value;return select}
|
|
11
|
+
function formatNumberInput(value,placeholder){const input=h("input");input.type="number";input.step="any";if(placeholder)input.placeholder=placeholder;if(value!==undefined&&value!==null)input.value=String(value);return input}
|
|
12
|
+
const formatNumberValue=input=>input.value.trim()===""?undefined:Number(input.value);function formatSegment(options,value,label,onChange){const segment=h("div","cx-seg");segment.setAttribute("role","group");segment.setAttribute("aria-label",label);segment._value=value;const sync=()=>{for(const button of segment.children){const on=button.dataset.cxValue===segment._value;button.setAttribute("aria-pressed",String(on));button.classList.toggle("on",on)}};for(const[key,text]of options){const button=gridButton(text,()=>{segment._value=key;sync();onChange(key)});button.dataset.cxValue=key;segment.appendChild(button)}
|
|
13
|
+
sync();return segment}
|
|
14
|
+
Object.assign(DataGrid.prototype,{formatToneOptions(rules){const labels=this.formatLabels,own=new Set(FORMAT_TONES);const used=JSON.stringify(rules).match(/"tone":"([a-z][a-z0-9-]{0,31})"/g) || [];
|
|
15
|
+
for(const entry of used)own.add(entry.slice(8,-1));return[...own].map(tone=>[tone,labels["formatTone"+tone[0].toUpperCase()+ tone.slice(1)]||tone])},renderFormatRule(editor,index){const labels=this.formatLabels,original=index>=0?editor.rules[index]:null,ids=new Set(editor.rules.map(rule=>rule.id));let id="rule-1";for(let n=1;ids.has(id);n++)id="rule-"+n;const firstNumber=this.columns.find(column=>column.kind==="number")||this.columns[0];const rule=original?JSON.parse(JSON.stringify(original)):{id,column:this.columns[0].id,style:{tone:"warning"}},style=rule.style||{};const tones=this.formatToneOptions(editor.rules),columnOptions=this.columns.map(column=>[column.id,column.title]);editor.back=()=>editor.list(original?"edit:"+index:"add");setText(editor.title,original?fill(labels.formatEditRule,{rule:rule.label||fill(labels.formatRule,{number:index+1})}):labels.formatAddRule);const form=h("form","cx-format-form cx-fb"),name=h("input"),description=h("input");name.type=description.type="text";name.maxLength=200;description.maxLength=1000;name.value=rule.label||"";description.value=rule.description||"";let kind=style.bar?"bar":style.scale?"scale":style.bands?"bands":"highlight";const kinds=formatSegment([["highlight",labels.formatHighlight],["bar",labels.formatDataBar],["scale",labels.formatColorScale],["bands",labels.formatBands]],kind,labels.formatStyle,next=>{kind=next;if(kind!=="highlight"&&target.value==="row")target.value="cell";if(kind!=="highlight"&&this.columns.find(item=>item.id===column.value)?.kind!=="number")column.value=firstNumber.id;sync();renderStyle({},true)});kinds.classList.add("cx-format-kind");const column=formatSelect(columnOptions,rule.column),target=formatSelect([["cell",labels.formatTargetCell],["row",labels.formatTargetRow],["columns",labels.formatTargetColumns]],rule.target||"cell");const chosen=h("fieldset","cx-format-columns"),legend=h("legend");setText(legend,labels.formatTargetColumns);chosen.appendChild(legend);for(const item of this.columns){const check=h("input");check.type="checkbox";check.value=item.id;check.checked=(rule.columns||[]).includes(item.id);chosen.appendChild(gridInputLabel(item.title,check))}
|
|
16
|
+
const when=h("input"),conditions=h("div","cx-format-conditions"),styleBox=h("div","cx-format-style");when.type="checkbox";when.checked=!!rule.when;const builder=this.formatConditionBuilder(conditions,rule.when||{operator:"and",filters:[],groups:[]});const columnField=formatField(labels.formatName,name),targetField=formatField(labels.formatApplyTo,target),sourceField=formatField(this.labels.gridColumn,column);const sync=()=>{target.querySelector('option[value="row"]').hidden=kind!=="highlight";sourceField.hidden=target.value==="row"||target.value==="columns"&&kind==="highlight";chosen.hidden=target.value!=="columns";conditions.hidden=!when.checked};target.addEventListener("change",sync);when.addEventListener("change",()=>{sync();if(when.checked)builder.focus()});let readStyle=()=>({});const extras=current=>({...(current.className?{className:current.className}:{}),...(current.css?{css:current.css}:{})});const renderStyle=(current,fresh)=>{styleBox.replaceChildren();const tone=(value,fallback,text=labels.formatTone)=>{const select=formatSelect(tones,value||fallback);styleBox.appendChild(formatField(text,select));return select};if(kind==="highlight"){const toneSelect=formatSelect([["","—"],...tones],current.tone??(fresh?"warning":""));const emphasis=formatSelect([["fill",labels.formatFill],["text",labels.formatTextOnly],["strong",labels.formatStrong]],current.emphasis||"fill");const icons=[["auto",labels.formatAutomatic],["none",labels.formatNoIcon],...Object.entries(FORMAT_ICONS).map(([key,glyph])=>[key,glyph])];if(current.icon&&!icons.some(([name])=>name===current.icon))icons.push([current.icon,current.icon]);const icon=formatSelect(icons,current.icon||"auto");const badge=h("input"),cue=h("input");badge.type=cue.type="text";badge.maxLength=cue.maxLength=64;badge.value=current.badge||"";cue.value=current.cue||"";styleBox.append(formatField(labels.formatTone,toneSelect),formatField(labels.formatEmphasis,emphasis),formatField(labels.formatIcon,icon),formatField(labels.formatBadge,badge),formatField(labels.formatCue,cue));readStyle=()=>{const out={};if(toneSelect.value){out.tone=toneSelect.value;if(emphasis.value!=="fill")out.emphasis=emphasis.value}
|
|
17
|
+
if(icon.value!=="auto")out.icon=icon.value;if(badge.value.trim())out.badge=badge.value.trim();if(cue.value.trim())out.cue=cue.value.trim();return{...out,...extras(current)}}}else if(kind==="bar"){const bar=current.bar||{},select=tone(bar.tone,"info");const low=formatNumberInput(bar.minimum,labels.formatAutomatic),high=formatNumberInput(bar.maximum,labels.formatAutomatic);styleBox.append(formatField(labels.formatLowest,low),formatField(labels.formatHighest,high));readStyle=()=>({...extras(current),bar:{tone:select.value,...(bar.showValue===false?{showValue:false}:{}),...(formatNumberValue(low)===undefined?{}:{minimum:formatNumberValue(low)}),...(formatNumberValue(high)===undefined?{}:{maximum:formatNumberValue(high)})}})}else if(kind==="scale"){const valueLabel=text=>text+" · "+this.labels.gridConditionValue;const stops=current.scale?current.scale.stops:[],first=stops[0]||{},last=stops[stops.length-1]||{},middle=stops.length===3?stops[1]:null;const lowTone=tone(first.tone,"success",labels.formatLowest),low=formatNumberInput(first.type==="number"?first.value:undefined,labels.formatAutomatic);styleBox.appendChild(formatField(valueLabel(labels.formatLowest),low,"cx-format-value"));const midTone=formatSelect([["","—"],...tones],middle?middle.tone:""),mid=formatNumberInput(middle&&middle.type==="number"?middle.value:undefined,"50%");styleBox.append(formatField(labels.formatMidpoint,midTone),formatField(valueLabel(labels.formatMidpoint),mid,"cx-format-value"));const highTone=tone(last.tone,"danger",labels.formatHighest),high=formatNumberInput(last.type==="number"?last.value:undefined,labels.formatAutomatic);styleBox.appendChild(formatField(valueLabel(labels.formatHighest),high,"cx-format-value"));const stop=(input,type,toneValue,fallback,previous)=>formatNumberValue(input)===undefined?
|
|
18
|
+
previous&&previous.type==="percent"?{type:"percent",value:previous.value,tone:toneValue}:{type,...(fallback===undefined?{}:{value:fallback}),tone:toneValue}:
|
|
19
|
+
{type:"number",value:formatNumberValue(input),tone:toneValue};readStyle=()=>({...extras(current),scale:{stops:[stop(low,"min",lowTone.value,undefined,stops.length?first:null),...(midTone.value?[stop(mid,"percent",midTone.value,50,middle)]:[]),stop(high,"max",highTone.value,undefined,stops.length?last:null)]}})}else this.formatBandsEditor(styleBox,current.bands||[{upperBound:100,tone:"success"},{tone:"danger"}],tones,read=>{readStyle=()=>({...extras(current),...(current.icon&¤t.bands?{icon:current.icon}:{}),bands:read()})})};renderStyle(style,!original);const whenField=gridInputLabel(labels.formatOnlyRowsWhere,when);whenField.classList.add("cx-format-when");form.append(columnField,formatField(labels.formatDescription,description),formatField(labels.formatStyle,kinds,"cx-format-kinds"),targetField,sourceField,chosen,whenField,conditions,styleBox);sync();const save=()=>{const next={id:rule.id};if(name.value.trim())next.label=name.value.trim();if(description.value.trim())next.description=description.value.trim();if(rule.enabled===false)next.enabled=false;if(target.value!=="cell")next.target=target.value;if(!sourceField.hidden)next.column=column.value;if(target.value==="columns")next.columns=[...chosen.querySelectorAll("input:checked")].map(check=>check.value);if(rule.details===false)next.details=false;if(rule.stopIfTrue)next.stopIfTrue=true;try{if(when.checked)next.when=builder.read();next.style=readStyle();const rules=editor.rules.slice();if(original)rules[index]=next;else rules.push(next);formatCompile(rules,this);editor.rules=rules}catch(error){setText(editor.status,labels.formatInvalid+" "+String(error.message).replace(/^CanopyX: /, "")); return; }
|
|
20
|
+
editor.apply().then(ok=>editor.list(ok?"edit:" +(original?index:editor.rules.length-1):null))};form.addEventListener("submit",event=>{event.preventDefault();save()});editor.body.replaceChildren(form);editor.foot.replaceChildren(gridButton(labels.formatCancel,()=>editor.back()),gridButton(labels.formatSave,save));setText(editor.status,"");name.focus({preventScroll:true})}});const FORMAT_FILTER_OPERATORS=["eq","ne","contains","startsWith","in","gt","ge","lt","le","isNull","isNotNull"];const FORMAT_EXTRA_OPERATORS={text:["notContains","endsWith","notIn","like","notLike","matches","notMatches"],number:["notIn","between","betweenExclusive"],datetime:["between","betweenExclusive","olderThan","withinLast"]};Object.assign(DataGrid.prototype,{formatConditionBuilder(container,initial){const labels=this.formatLabels,grid=this,names={notContains:labels.formatNotContains,endsWith:labels.formatEndsWith,notIn:labels.formatNotIn,like:labels.formatLike,notLike:labels.formatNotLike,matches:labels.formatMatches,notMatches:labels.formatNotMatches,between:labels.formatBetween,betweenExclusive:labels.formatBetweenExclusive,olderThan:labels.formatOlderThan,withinLast:labels.formatWithinLast},ranged=operator=>operator==="between"||operator==="betweenExclusive";const invalid=()=>{throw new Error(grid.labels.gridInvalidFilter)};const dateText=value=>{const date=new Date(value);return Number.isFinite(date.getTime())?new Date(date.getTime()-(grid.local?date.getTimezoneOffset()*60000:0)).toISOString().slice(0,-1):""};const dateValue=text=>{const date=gridDateInputMoment(text,grid.local);return date?date.toISOString():invalid()};const typed=(column,text)=>column.kind==="number"?text.trim()&&Number.isFinite(Number(text))?Number(text):invalid():column.kind==="datetime"?dateValue(text):text;const extend=(row,predicate)=>{const fields=row._filter,upper=h("input","cx-grid-filter-value"),upperField=gridInputLabel(this.labels.gridConditionUpperValue,upper);upperField.classList.add("cx-condition-upper");upperField.hidden=true;const exact=gridButton("Aa",()=>{exact.setAttribute("aria-pressed",String(exact.getAttribute("aria-pressed")!=="true"));fields.edited=true});exact.classList.add("cx-format-case");exact.setAttribute("aria-label",labels.formatMatchCase);exact.title=labels.formatMatchCase;exact.setAttribute("aria-pressed",String(!!predicate?.caseSensitive));fields.input.closest("label").after(upperField);row.insertBefore(exact,row.querySelector(":scope > button.icon"));const definition=()=>this.columns.find(item=>item.id===fields.column.value);const options=()=>{for(const option of[...fields.operator.options])if(option.dataset.cxFormat)option.remove();for(const name of FORMAT_EXTRA_OPERATORS[definition().kind]||[]){const option=gridOption(name,names[name]);option.dataset.cxFormat="1";fields.operator.appendChild(option)}
|
|
21
|
+
exact.hidden=definition().kind!=="text"};const update=()=>{const operator=fields.operator.value,kind=definition().kind,relative=operator==="olderThan"||operator==="withinLast";if(operator==="notIn"){fields.input.closest("label").hidden=fields.input.disabled=true;fields.multiple.parentNode.hidden=false}
|
|
22
|
+
upperField.hidden=upper.disabled=!ranged(operator);if(kind==="datetime"){fields.input.type=relative?"number":"datetime-local";upper.type="datetime-local"}
|
|
23
|
+
else upper.type=kind==="number"?"number":"text";upper.step=fields.input.step="any";if(relative)fields.input.min="0"};fields.column.addEventListener("change",()=>{options();update()});fields.operator.addEventListener("change",update);upper.addEventListener("input",()=>{fields.edited=true});options();if(predicate&&names[predicate.operator]){fields.operator.value=predicate.operator;const kind=definition().kind,show=value=>kind==="datetime"&&predicate.operator!=="olderThan"&&predicate.operator!=="withinLast"?dateText(value):String(value);if(predicate.operator==="notIn")predicate.values.forEach(fields.multiple._add);else{fields.input.value=show(predicate.values[0]);if(ranged(predicate.operator))upper.value=show(predicate.values[1])}}
|
|
24
|
+
update();row._format={upper,exact,source:predicate}};const read=row=>{const fields=row._filter,operator=fields.operator.value,column=this.columns.find(item=>item.id===fields.column.value);const source=row._format.source;if(source&&!fields.edited)return[JSON.parse(JSON.stringify(source))];let predicates;if(operator==="notIn"){const values=[...fields.multiple._values.values()];if(!values.length)invalid();predicates=[{column:column.id,operator,values}]}
|
|
25
|
+
else if(ranged(operator))predicates=[{column:column.id,operator,values:[typed(column,fields.input.value),typed(column,row._format.upper.value)]}];else if(operator==="olderThan"||operator==="withinLast")predicates=[{column:column.id,operator,values:[Number(fields.input.value.trim()?fields.input.value:invalid())]}];else if(names[operator])predicates=[{column:column.id,operator,values:[fields.input.value||invalid()]}];else predicates=[].concat(this.readGridFilter(row)).map(predicate=>({...predicate,values:predicate.values.slice()}));if(column.kind==="text"&&row._format.exact.getAttribute("aria-pressed")==="true")predicates.forEach(predicate=>{predicate.caseSensitive=true});if(source&&source.column===column.id)for(const predicate of predicates)for(const name of["type","culture"])if(source[name]!==undefined)predicate[name]=source[name];return predicates};const words=block=>{const data=block._group;[...data.rows.children].forEach((row,index)=>setText(row.querySelector(".w"),index===0?this.labels.gridWhere:data.segment._value==="and"?this.labels.gridAnd:data.segment._value==="or"?this.labels.gridOr:labels.formatNone.toLowerCase()));for(const child of data.children.children)words(child)};const group=(parent,definition,depth)=>{const block=h("div",depth?"cx-grid-filter-group":"cx-filter-root"),heading=h("div","cx-group-h"),rows=h("div","cx-condition-rows"),children=h("div","cx-grid-filter-group-children"),actions=h("div","cx-adds");const segment=formatSegment([["and",this.labels.gridFilterAll],["or",this.labels.gridFilterAny],["none",labels.formatNone]],definition?.operator||"and",this.labels.gridFilterMatch,()=>words(root));heading.append(segment,document.createTextNode(" " +(depth?this.labels.gridOfThese:this.labels.gridOfTheseAreTrue)));block._group={segment,rows,children};block.append(heading,rows,children,actions);parent.appendChild(block);const add=predicate=>{if(container.querySelectorAll(".cx-grid-filter").length>=64)return null;this.addGridFilter(rows,predicate&&{column:predicate.column,operator:predicate.operator,values:predicate.values||[]});const row=rows.lastElementChild;row._design=true;row.classList.add("cx-cond");row._filter.type.closest("label").hidden=row._filter.type.value==="string";const word=h("span","w");row.prepend(word);const remove=row.querySelector(":scope > button");if(remove){remove.replaceChildren(gridIcon("close"));remove.classList.add("icon");remove.setAttribute("aria-label",this.labels.gridDeleteCondition);remove.addEventListener("click",()=>words(root))}
|
|
26
|
+
extend(row,predicate);words(root||block);return row};const condition=gridButton(this.labels.gridAddCondition,()=>add(null)?._filter.column.focus());condition.classList.add("cx-link");const nested=gridButton(this.labels.gridAddGroup,()=>{group(children,null,depth+1);words(root)});nested.classList.add("cx-link");nested.disabled=depth>=2;actions.append(condition,nested);if(depth){const remove=gridDecorateButton(gridButton("",()=>{block.remove();words(root)}),"close",true);remove.classList.add("icon");remove.setAttribute("aria-label",this.labels.gridDeleteFilterGroup);heading.appendChild(remove)}
|
|
27
|
+
(definition?.filters||[]).forEach(add);(definition?.groups||[]).forEach(child=>group(children,child,depth+1));if(!depth&&!rows.children.length&&!children.children.length)add(null);return block};const readBlock=block=>{const{segment,rows,children}=block._group,filters=[],groups=[...children.children].map(readBlock).filter(Boolean);for(const row of rows.children){const predicates=read(row);if(segment._value!=="and"&&predicates.length>1)groups.push({operator:"and",filters:predicates,groups:[]});else filters.push(...predicates)}
|
|
28
|
+
return filters.length||groups.length?{operator:segment._value,filters,groups}:null};let root=null;root=group(container,initial,0);words(root);return{read:()=>readBlock(root)||invalid(),focus:()=>root.querySelector("select")?.focus()}},formatBandsEditor(box,bands,tones,register){const labels=this.formatLabels,list=h("div","cx-format-bands"),items=bands.map(band=>({...band}));const render=()=>{list.replaceChildren();items.forEach((band,index)=>{const row=h("div","cx-format-band"),last=index===items.length-1,tone=formatSelect(tones,band.tone),label=h("input");if(last&&band.upperBound===undefined){const above=h("span","cx-format-above");setText(above,labels.formatAbove);row.appendChild(above)}
|
|
29
|
+
else{const bound=formatNumberInput(band.upperBound);bound.addEventListener("input",()=>{band.upperBound=formatNumberValue(bound)});row.appendChild(formatField(labels.formatUpTo,bound))}
|
|
30
|
+
label.type="text";label.maxLength=64;label.value=band.label||"";label.placeholder=labels.formatCue;tone.addEventListener("change",()=>{band.tone=tone.value});label.addEventListener("input",()=>{band.label=label.value.trim()||undefined});row.append(formatField(labels.formatTone,tone),formatField(labels.formatCue,label));if(items.length>1)row.appendChild(formatIconButton(labels.formatRemoveBand,"close",()=>{items.splice(index,1);render()}));list.appendChild(row)});add.disabled=items.length>=16};const add=gridButton(labels.formatAddBand,()=>{if(items.length>=16)return;items.splice(items.length-1,0,{tone:"warning"});render();list.querySelector(".cx-format-band:nth-last-child(2) input")?.focus()});add.classList.add("cx-link");add.disabled=items.length>=16;render();box.append(list,add);register(()=>items.map(band=>({...(band.upperBound===undefined?{}:{upperBound:band.upperBound}),tone:band.tone,...(band.label?{label:band.label}:{}),...(band.icon?{icon:band.icon}:{})})))}});installed.add(context.DataGrid.prototype)}};(global.CanopyXParts||=[]).push(["format-editor",part])})(typeof window!=="undefined"?window:globalThis);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
.cx-data-grid .cx-row .cx-cell.cx-fmt-fill,.cx-data-grid .cx-row .cx-cell.cx-fmt-strong{background-color:color-mix(in srgb,var(--cx-fmt) 13%,var(--cx-surface));color:color-mix(in srgb,var(--cx-fmt) 82%,var(--cx-text));}
|
|
2
|
+
.cx-data-grid .cx-row .cx-cell.cx-fmt-strong{font-weight:600;}
|
|
3
|
+
.cx-data-grid .cx-row .cx-cell.cx-fmt-text{color:color-mix(in srgb,var(--cx-fmt) 82%,var(--cx-text));}
|
|
4
|
+
.cx-data-grid .cx-row .cx-cell.cx-fmt-scale{background-color:color-mix(in srgb,var(--cx-fmt-scale) 18%,var(--cx-surface));}
|
|
5
|
+
.cx-data-grid .cx-row.cx-fmt-row,.cx-data-grid .cx-row.cx-fmt-row [data-cx-pin]{background:color-mix(in srgb,var(--cx-fmt) 8%,var(--cx-surface));}
|
|
6
|
+
.cx-data-grid .cx-row.cx-fmt-row:hover,.cx-data-grid .cx-row.cx-fmt-row:hover [data-cx-pin]{background:color-mix(in srgb,var(--cx-fmt) 12%,var(--cx-surface));}
|
|
7
|
+
.cx-data-grid .cx-row.cx-fmt-row[aria-selected="true"],.cx-data-grid .cx-row.cx-fmt-row[aria-selected="true"] [data-cx-pin]{background:var(--cx-selected-bg);}
|
|
8
|
+
.cx-data-grid .cx-row.cx-fmt-row > .cx-cell:first-child{box-shadow:inset var(--cx-size-3px) 0 0 var(--cx-fmt);}
|
|
9
|
+
.cx-fmt-bar{flex:1 1 var(--cx-size-24px);min-width:var(--cx-size-24px);max-width:var(--cx-size-180px);height:var(--cx-size-8px);border-radius:var(--cx-size-4px);overflow:hidden;background:color-mix(in srgb,var(--cx-text) 9%,transparent);pointer-events:none;}
|
|
10
|
+
.cx-fmt-bar::before{content:"";display:block;width:var(--cx-fmt-w);height:100%;border-radius:inherit;background:color-mix(in srgb,var(--cx-fmt) 78%,var(--cx-surface));}
|
|
11
|
+
.cx-fmt-novalue > .cx-text{position:absolute;width:var(--cx-size-1px);height:var(--cx-size-1px);overflow:hidden;clip:rect(0 0 0 0);}
|
|
12
|
+
.cx-fmt-icon{flex:none;color:var(--cx-fmt,currentColor);font-weight:700;line-height:1;}
|
|
13
|
+
.cx-fmt-badge{flex:none;}
|
|
14
|
+
.cx-fmt-cue{flex:none;color:var(--cx-text-2);font-size:var(--cx-size-12d5px);}
|
|
15
|
+
.cx-data-grid .cx-hidden-fields.cx-fmt-flag{color:var(--cx-fmt);font-weight:700;text-decoration:underline var(--cx-size-2px) var(--cx-fmt);text-underline-offset:var(--cx-size-3px);}
|
|
16
|
+
.cx-drawer dd:is(.cx-fmt-fill,.cx-fmt-strong,.cx-fmt-scale){display:flex;flex-wrap:wrap;align-items:center;gap:var(--cx-size-6px);margin:var(--cx-size-neg-2px) 0;padding:var(--cx-size-2px) var(--cx-size-6px);border-radius:var(--cx-radius-sm);}
|
|
17
|
+
.cx-drawer dd.cx-fmt-fill,.cx-drawer dd.cx-fmt-strong{background:color-mix(in srgb,var(--cx-fmt) 13%,var(--cx-surface));color:color-mix(in srgb,var(--cx-fmt) 82%,var(--cx-text));}
|
|
18
|
+
.cx-drawer dd.cx-fmt-strong{font-weight:600;}
|
|
19
|
+
.cx-drawer dd.cx-fmt-text{color:color-mix(in srgb,var(--cx-fmt) 82%,var(--cx-text));}
|
|
20
|
+
.cx-drawer dd.cx-fmt-scale{background:color-mix(in srgb,var(--cx-fmt-scale) 18%,var(--cx-surface));}
|
|
21
|
+
.cx-drawer dd:has(> .cx-fmt-icon,> .cx-fmt-cue,> .cx-fmt-badge,> .cx-fmt-bar){display:flex;flex-wrap:wrap;align-items:center;gap:var(--cx-size-6px);}
|
|
22
|
+
.cx-drawer .cx-drawer-h.cx-fmt-accent{box-shadow:inset var(--cx-size-3px) 0 0 var(--cx-fmt);background:color-mix(in srgb,var(--cx-fmt) 8%,var(--cx-surface));}
|
|
23
|
+
.cx-drawer .cx-drawer-h > .cx-fmt-icon{margin-top:var(--cx-size-4px);font-size:var(--cx-size-17px);}
|
|
24
|
+
.cx-format-legend{flex:none;display:flex;flex-wrap:wrap;align-items:center;gap:var(--cx-size-6px) var(--cx-size-16px);margin:0 0 var(--cx-size-8px);padding:0;list-style:none;font-size:var(--cx-size-12d5px);color:var(--cx-text-2);}
|
|
25
|
+
.cx-format-key{display:inline-flex;align-items:center;gap:var(--cx-size-6px);min-width:0;}
|
|
26
|
+
.cx-format-swatch{position:relative;isolation:isolate;display:inline-grid;place-items:center;flex:none;min-width:var(--cx-size-18px);height:var(--cx-size-18px);border-radius:var(--cx-radius-sm);border:var(--cx-size-1px) solid var(--cx-line);font-size:var(--cx-size-11px);}
|
|
27
|
+
.cx-format-swatch.cx-fmt-fill,.cx-format-swatch.cx-fmt-strong,.cx-format-swatch.cx-fmt-text{background:color-mix(in srgb,var(--cx-fmt) 13%,var(--cx-surface));border-color:color-mix(in srgb,var(--cx-fmt) 35%,var(--cx-surface));}
|
|
28
|
+
.cx-format-swatch.cx-fmt-gradient{width:var(--cx-size-36px);background:var(--cx-fmt-scale);opacity:.55;}
|
|
29
|
+
.cx-format-swatch.cx-fmt-barred{width:var(--cx-size-36px);padding:0 var(--cx-size-3px);}
|
|
30
|
+
.cx-format-swatch .cx-fmt-bar{width:100%;max-width:none;}
|
|
31
|
+
.cx-format-swatch.cx-format-badge{border:0;min-width:0;height:auto;}
|
|
32
|
+
@media print{
|
|
33
|
+
.cx-fmt-fill,.cx-fmt-strong,.cx-fmt-scale,.cx-fmt-row,.cx-fmt-bar::before,.cx-fmt-accent,.cx-format-swatch{print-color-adjust:exact;-webkit-print-color-adjust:exact;}
|
|
34
|
+
.cx-format-legend{display:none;}
|
|
35
|
+
}
|
|
36
|
+
@media (forced-colors:active){
|
|
37
|
+
.cx-data-grid .cx-row.cx-fmt-row > .cx-cell:first-child,.cx-drawer .cx-drawer-h.cx-fmt-accent{forced-color-adjust:none;box-shadow:inset var(--cx-size-3px) 0 0 Highlight;}
|
|
38
|
+
.cx-fmt-bar::before{forced-color-adjust:none;background:Highlight;}
|
|
39
|
+
.cx-fmt-fill,.cx-fmt-strong,.cx-fmt-scale{outline:var(--cx-size-1px) dashed CanvasText;outline-offset:var(--cx-size-neg-3px);}
|
|
40
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
(function(global){"use strict";const installed=new WeakSet();const part={version:"6f9f3c51b0ad2a0e",install(context){if(installed.has(context.DataGrid.prototype))return;const{DataGrid,h,setText,setClass,fill,gridFreeze,gridDateInputMoment,cellValue,cellText,foldText,toneOf,numberFormat,gridButton,gridOption,gridInputLabel,gridIcon,gridDecorateButton,GRID_REPORTING,loadPart,definePart,announceMenu,boundedPattern,wildcardMatch,svgIcon}=context;const FORMAT_OPERATORS={eq:1,ne:1,gt:1,ge:1,lt:1,le:1,contains:1,notContains:1,startsWith:1,endsWith:1,like:1,notLike:1,matches:1,notMatches:1,in:-1,notIn:-1,between:2,betweenExclusive:2,isNull:0,isNotNull:0,olderThan:1,withinLast:1};const FORMAT_TYPES=["text","number","boolean","date"],FORMAT_DAY=86400000;function formatError(message){throw new Error("CanopyX: "+message)}
|
|
2
|
+
function formatKeys(value,keys,what){if(!value||typeof value!=="object"||Array.isArray(value)||Object.keys(value).some(key=>!keys.includes(key)))formatError("invalid "+what+".")}
|
|
3
|
+
const formatCultures=new Map();function formatCulture(culture){let entry=formatCultures.get(culture);if(!entry){let tag;try{tag=Intl.getCanonicalLocales(culture)[0]}catch(_){formatError("unknown culture '"+culture+"'.")}
|
|
4
|
+
const parts=new Intl.NumberFormat(tag).formatToParts(12345.6),part=type=>(parts.find(item=>item.type===type)||{}).value;entry={tag,group:part("group")||",",decimal:part("decimal")||".",accent:new Intl.Collator(tag,{sensitivity:"accent"}),variant:new Intl.Collator(tag,{sensitivity:"variant"})};if(formatCultures.size<64)formatCultures.set(culture,entry)}
|
|
5
|
+
return entry}
|
|
6
|
+
function formatNumber(text,culture){let value=String(text).trim().replace(/[\s ']/g, "");
|
|
7
|
+
if(culture)value=value.split(culture.group).join("").replace(culture.decimal,".");return /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i.test(value) && Number.isFinite(Number(value)) ? Number(value) : null;
|
|
8
|
+
}
|
|
9
|
+
function formatCoerce(raw,type,culture){if(raw==null)return null;if(type==="number")return typeof raw==="number"?Number.isFinite(raw)?raw:null:typeof raw==="boolean"?+raw:formatNumber(raw,culture);if(type==="date"){const iso=typeof raw==="string"&&/^\d{4}-\d\d-\d\d(?:T\d\d:\d\d(?::\d\d(?:\.\d+)?)?(Z|[+-]\d\d:\d\d)?)?$/.exec(raw.trim());
|
|
10
|
+
const time=typeof raw==="number"?raw:iso?Date.parse(raw.trim()+(iso[1]||raw.trim().length===10?"":"Z")):NaN;return Number.isFinite(time)?time:null}
|
|
11
|
+
if(type==="boolean"){if(typeof raw==="boolean")return raw;if(typeof raw==="number")return raw!==0;const word=String(raw).trim();return /^(?:true|yes|on|1)$/i.test(word) ? true : /^(?:false|no|off|0)$/i.test(word) ? false : null;
|
|
12
|
+
}
|
|
13
|
+
return type==="text"?String(raw):raw}
|
|
14
|
+
function formatPredicate(predicate,columns,budget){if(++budget.predicates>64)formatError("a formatting rule has at most 64 conditions.");formatKeys(predicate,["column","operator","values","valueColumn","type","caseSensitive","culture"],"formatting condition");const column=columns.get(predicate.column),op=predicate.operator,arity=FORMAT_OPERATORS[op],values=predicate.values??[];const other=predicate.valueColumn===undefined?null:columns.get(predicate.valueColumn)||formatError("a condition compares with an undeclared column.");if(!column||arity===undefined||!Array.isArray(values)||predicate.caseSensitive!==undefined&&typeof predicate.caseSensitive!=="boolean"||
|
|
15
|
+
predicate.type!==undefined&&!FORMAT_TYPES.includes(predicate.type)||predicate.culture!==undefined&&(typeof predicate.culture!=="string"||!predicate.culture||predicate.culture.length>64))
|
|
16
|
+
formatError("invalid formatting condition.");const patterned=op==="like"||op==="notLike"||op==="matches"||op==="notMatches";if(other?values.length||arity!==1||op==="olderThan"||op==="withinLast"||patterned:arity<0?values.length<1||values.length>1000:values.length!==arity)
|
|
17
|
+
formatError("formatting condition '"+op+"' has the wrong number of values.");if(values.some(value=>!(typeof value==="string"&&value.length<=4096||typeof value==="boolean"||Number.isFinite(value))))
|
|
18
|
+
formatError("condition values are text, finite numbers or booleans.");const culture=predicate.culture?formatCulture(predicate.culture):null,exact=!!predicate.caseSensitive;const type=op==="olderThan"||op==="withinLast"?"date":predicate.type||(column.kind==="number"?"number":column.kind==="datetime"?"date":null);if((op==="olderThan"||op==="withinLast")&&!(typeof values[0]==="number"&&values[0]>=0&&values[0]<=36500))
|
|
19
|
+
formatError("relative dates take a number of days from 0 to 36500.");const text=value=>typeof value==="string"?exact?value:culture?value.toLocaleLowerCase(culture.tag):foldText(value):value;const read=(cells,id)=>text(formatCoerce(cellValue(cells[id]),type,culture));const collator=culture&&(exact?culture.variant:culture.accent);const order=(a,b)=>typeof a==="string"&&typeof b==="string"&&collator?collator.compare(a,b):a<b?-1:a>b?1:0;let operands=[],pattern=null;if(patterned){const source=String(values[0]);if(source.length>256)formatError("patterns have at most 256 characters.");try{pattern=op==="like"||op==="notLike"?{test:value=>wildcardMatch(source,value,exact)}:boundedPattern(source,exact)}
|
|
20
|
+
catch(_){formatError("invalid pattern '"+source+"'.")}}else if(op==="olderThan"||op==="withinLast")operands=[values[0]*FORMAT_DAY];else operands=values.map(value=>{const typed=text(formatCoerce(value,type,culture));if(typed===null)formatError("value '"+value+"' does not convert to " +(type||"text")+ ".");return typed});if((op==="between"||op==="betweenExclusive")&&order(operands[0],operands[1])>0)formatError("a range starts with its lower value.");const id=column.id,otherId=other&&other.id,set=arity<0&&!collator?new Set(operands):null;const member=value=>set?set.has(value):operands.some(operand=>typeof operand===typeof value&&order(value,operand)===0);budget.relative||=op==="olderThan"||op==="withinLast";return row=>{const value=read(row.cells,id);if(op==="isNull")return value===null;if(op==="isNotNull"||value===null)return op==="isNotNull"&&value!==null;const operand=other?read(row.cells,otherId):operands[0];if(operand===null)return false;switch(op){case "eq":return order(value,operand)===0&&typeof value===typeof operand;case "ne":return!(order(value,operand)===0&&typeof value===typeof operand);case "gt":return order(value,operand)>0;case "ge":return order(value,operand)>=0;case "lt":return order(value,operand)<0;case "le":return order(value,operand)<=0;case "in":return member(value);case "notIn":return!member(value);case "between":return order(value,operand)>=0&&order(value,operands[1])<=0;case "betweenExclusive":return order(value,operand)>0&&order(value,operands[1])<0;case "contains":return String(value).includes(String(operand));case "notContains":return!String(value).includes(String(operand));case "startsWith":return String(value).startsWith(String(operand));case "endsWith":return String(value).endsWith(String(operand));case "like":case "matches":return pattern.test(String(value).slice(0,4096));case "notLike":case "notMatches":return!pattern.test(String(value).slice(0,4096));case "olderThan":return value<Date.now()- operand;case "withinLast":{const now=Date.now();return value>=now-operand&&value<=now}}
|
|
21
|
+
return false}}
|
|
22
|
+
function formatGroup(group,columns,budget,depth){if(depth>8||++budget.groups>64)formatError("formatting conditions exceed eight levels or 64 groups.");formatKeys(group,["operator","filters","groups"],"formatting condition group");const filters=group.filters??[],groups=group.groups??[];if(!["and","or","none"].includes(group.operator)||!Array.isArray(filters)||!Array.isArray(groups)||!filters.length&&!groups.length)
|
|
23
|
+
formatError("invalid or empty formatting condition group.");const tests=[...filters.map(filter=>formatPredicate(filter,columns,budget)),...groups.map(child=>formatGroup(child,columns,budget,depth+1))];const count=tests.length;if(group.operator==="and")return row=>{for(let i=0;i<count;i++)if(!tests[i](row))return false;return true};const any=row=>{for(let i=0;i<count;i++)if(tests[i](row))return true;return false};return group.operator==="or"?any:row=>!any(row)}
|
|
24
|
+
const FORMAT_ICONS={up:"↑",down:"↓",check:"✓",cross:"✕",warning:"⚠︎",info:"ⓘ",dot:"●",circle:"○",triangle:"▲",square:"■",diamond:"◆",flag:"⚑",star:"★",minus:"−",plus:"+",clock:"◷"};const FORMAT_TONE_ICONS={danger:"cross",warning:"warning",success:"check",info:"info"};const FORMAT_LABELS={formatRules:"Conditional formatting",formatLegend:"Formatting legend",formatUpTo:"Up to",formatAbove:"Above",formatHiddenFlagged:"{count} hidden fields formatted: {rules}"};function formatToneVar(tone){return tone==="neutral"?"var(--cx-text-2)":tone==="muted"?"var(--cx-muted)":"var(--cx-tone-"+tone+",var(--cx-text-2))"}
|
|
25
|
+
function formatTone(value){if(typeof value!=="string"||!toneOf(value).cls)formatError("'"+value+"' is not a tone name.");return value}
|
|
26
|
+
const formatText=(value,limit)=>value===undefined||typeof value==="string"&&value.trim()&&value.length<=limit&&!/[\u0000-\u001f\u007f]/.test(value) ? value : formatError("formatting text must have 1 to " + limit + " visible characters.");
|
|
27
|
+
const formatFinite=value=>value===undefined||Number.isFinite(value)&&Math.abs(value)<=Number.MAX_SAFE_INTEGER?value:formatError("formatting bounds are finite numbers.");function formatIcon(value){if(value===undefined||value==="auto"||value==="none"||Object.hasOwn(FORMAT_ICONS,value))return value;if(typeof value!=="string"||!value.trim()||[...value].length>4||/[\u0000-\u001f\u007f<>]/.test(value)) formatError("an icon is a known name or up to four characters.");
|
|
28
|
+
return value}
|
|
29
|
+
function formatStyle(style,rule){formatKeys(style,["tone","emphasis","icon","badge","cue","bar","scale","bands","className","css"],"formatting style");if(style.tone!==undefined)formatTone(style.tone);if(style.emphasis!==undefined&&!["fill","text","strong"].includes(style.emphasis))formatError("emphasis is fill, text or strong.");formatIcon(style.icon);formatText(style.badge,64);formatText(style.cue,64);if(style.className!==undefined&&(typeof style.className!=="string"||!style.className.trim()||style.className.length>1024||/[\u0000-\u001f\u007f-\u009f]/.test(style.className)))
|
|
30
|
+
formatError("invalid formatting className.");if(style.css!==undefined){formatKeys(style.css,Object.keys(style.css),"formatting css");const entries=Object.entries(style.css);if(entries.length>16||entries.some(([name,value])=>!/^-{0,2}[a-z][a-z0-9-]{0,63}$/.test(name) || typeof value !== "string" || value.length > 200 ||
|
|
31
|
+
/url\s*\(|image-set|image\(|src\s*\(|expression|javascript:|@import|[;{}<>\\"'&]/i.test(value))) formatError("formatting css takes up to 16 plain declarations.");
|
|
32
|
+
}
|
|
33
|
+
const visuals=["bar","scale","bands"].filter(name=>style[name]!==undefined);if(visuals.length>1)formatError("a rule draws one of a data bar, a colour scale or threshold bands.");if(visuals.length&&(rule.target==="row"||!rule.column))formatError("bars, scales and bands read the rule's column and paint cells.");if(style.bar!==undefined){formatKeys(style.bar,["minimum","maximum","tone","showValue"],"data bar");formatFinite(style.bar.minimum);formatFinite(style.bar.maximum);if(style.bar.tone!==undefined)formatTone(style.bar.tone);if(style.bar.showValue!==undefined&&typeof style.bar.showValue!=="boolean"||style.bar.minimum>=style.bar.maximum)formatError("invalid data bar.")}
|
|
34
|
+
if(style.scale!==undefined){formatKeys(style.scale,["stops"],"colour scale");const stops=style.scale.stops;if(!Array.isArray(stops)||stops.length<2||stops.length>3)formatError("a colour scale has two or three stops.");for(const stop of stops){formatKeys(stop,["type","value","tone"],"colour scale stop");formatTone(stop.tone);formatFinite(stop.value);if(!["min","max","number","percent"].includes(stop.type)||(stop.type==="number"||stop.type==="percent")!==(stop.value!==undefined)||
|
|
35
|
+
stop.type==="percent"&&(stop.value<0||stop.value>100))formatError("invalid colour scale stop.")}}
|
|
36
|
+
if(style.bands!==undefined){const bands=style.bands;if(!Array.isArray(bands)||!bands.length||bands.length>16)formatError("threshold bands are 1 to 16 bands.");bands.forEach((band,index)=>{formatKeys(band,["upperBound","tone","label","icon"],"threshold band");formatTone(band.tone);formatText(band.label,64);formatIcon(band.icon);formatFinite(band.upperBound);if(band.upperBound===undefined&&index!==bands.length-1||index&&band.upperBound!==undefined&&bands[index-1].upperBound>=band.upperBound)
|
|
37
|
+
formatError("threshold bands rise, and only the last may be open.")})}
|
|
38
|
+
if(!["tone","icon","badge","cue","className","css",...visuals].some(name=>style[name]!==undefined&&style[name]!=="none"))
|
|
39
|
+
formatError("a formatting rule needs a style.")}
|
|
40
|
+
function formatCompile(rules,grid){if(!Array.isArray(rules)||rules.length>64)formatError("a grid has at most 64 formatting rules.");const columns=new Map(grid.columns.map(column=>[column.id,column])),ids=new Set();return rules.map((rule,index)=>{formatKeys(rule,["id","label","description","enabled","when","column","target","columns","details","style","stopIfTrue"],"formatting rule");if(typeof rule.id!=="string"||!/^[A-Za-z0-9][\w.:-]{0,63}$/.test(rule.id) || ids.has(rule.id)) formatError("formatting rules need unique identifiers.");
|
|
41
|
+
ids.add(rule.id);formatText(rule.label,200);formatText(rule.description,1000);const target=rule.target??"cell";if(!["cell","row","columns"].includes(target)||["enabled","details","stopIfTrue"].some(name=>rule[name]!==undefined&&typeof rule[name]!=="boolean")||
|
|
42
|
+
rule.column!==undefined&&!columns.has(rule.column)||target==="cell"&&rule.column===undefined)
|
|
43
|
+
formatError("formatting rule '"+rule.id+"' needs a declared column for its target.");const targets=target==="columns"?rule.columns:target==="cell"?[rule.column]:[];if(target==="columns"?!Array.isArray(targets)||!targets.length||targets.length>64||new Set(targets).size!==targets.length||targets.some(id=>!columns.has(id)):rule.columns!==undefined)
|
|
44
|
+
formatError("formatting rule '"+rule.id+"' names its columns once each.");formatStyle(rule.style,{target,column:rule.column});const budget={predicates:0,groups:0},test=rule.when===undefined?null:formatGroup(rule.when,columns,budget,1);return{id:rule.id,index,label:rule.label,description:rule.description||rule.label||rule.id,enabled:rule.enabled!==false,test,target,column:rule.column,columns:targets,details:rule.details!==false,stop:!!rule.stopIfTrue,style:rule.style,relative:!!budget.relative}})}
|
|
45
|
+
function formatOptions(options){if(options===undefined)return{};formatKeys(options,["legend","editor"],"formatting options");if(["legend","editor"].some(name=>options[name]!==undefined&&typeof options[name]!=="boolean"))formatError("formatting options are booleans.");return{legend:!!options.legend,editor:!!options.editor}}
|
|
46
|
+
function formatStats(grid){const inline=grid.view.source.kind==="inline"&&(!grid.opts.source||grid.enhanceStore),key=inline?"inline:" +(grid.enhanceStore?.version??0)+ ":"+grid.view.rows.length:grid.queryEpoch+":" +(grid.snapshot?.revision||"")+ ":" +[...grid.pages.keys()].join(",");if(grid.formatStatsKey===key)return grid.formatStatsCache;const wanted=new Set(grid.formatCompiled.filter(rule=>rule.style.bar||rule.style.scale).map(rule=>rule.column)),stats=new Map();if(wanted.size){const rows=inline?grid.view.rows:[...grid.rowCache.values()].map(row=>row.h);for(const id of wanted){let min=Infinity,max=-Infinity;for(const row of rows){const value=formatCoerce(cellValue(row.cells[id]),"number");if(value!==null){if(value<min)min=value;if(value>max)max=value}}
|
|
47
|
+
stats.set(id,min<=max?{min,max}:null)}}
|
|
48
|
+
if(grid.formatStatsKey!==undefined&&wanted.size){grid.formatVersion++;if(grid.recordDrawer)DataGrid.refreshDrawerHooks(grid,grid.recordDrawer)}
|
|
49
|
+
grid.formatStatsKey=key;grid.formatStatsCache=stats;return stats}
|
|
50
|
+
function formatBound(stop,stats){return stop.type==="number"?stop.value:!stats?null:stop.type==="min"?stats.min:stop.type==="max"?stats.max:stats.min +(stats.max-stats.min)*stop.value / 100;
|
|
51
|
+
}
|
|
52
|
+
const formatMix=(a,b,t)=>t<=0?formatToneVar(a):t>=1?formatToneVar(b):"color-mix(in oklab,"+formatToneVar(b)+ " "+Math.round(t*100)+ "%,"+formatToneVar(a)+ ")";function formatResolve(rule,data,stats){const style=rule.style,out={tone:style.tone,emphasis:style.emphasis,icon:style.icon,badge:style.badge,cue:style.cue,className:style.className,css:style.css};if(style.bar||style.scale||style.bands){const value=formatCoerce(cellValue(data.cells[rule.column]),"number");if(value===null)return null;const range=stats.get(rule.column);if(style.bar){const low=style.bar.minimum??Math.min(0,range?range.min:0),high=style.bar.maximum??(range?range.max:low+1);out.bar={width:high>low?Math.max(0,Math.min(1,(value-low)/ (high - low))) : 0, tone: style.bar.tone || style.tone || "info", show: style.bar.showValue !== false };
|
|
53
|
+
out.tone=undefined}else if(style.scale){const stops=style.scale.stops.map(stop=>({at:formatBound(stop,range),tone:stop.tone})).filter(stop=>stop.at!==null).sort((a,b)=>a.at-b.at);if(stops.length<2)return null;const last=stops.length-1,upper=stops.findIndex(stop=>value<=stop.at),to=upper<0?last:Math.max(1,upper),from=stops[to-1],next=stops[to];out.scale=formatMix(from.tone,next.tone,next.at>from.at?(value-from.at)/ (next.at - from.at) : 1);
|
|
54
|
+
out.tone=undefined}else{const band=style.bands.find(item=>item.upperBound===undefined||value<=item.upperBound);if(!band)return null;out.tone=band.tone;out.icon=band.icon??style.icon;out.band=band.label}}
|
|
55
|
+
return out}
|
|
56
|
+
function formatMerge(into,rule,style){const target=into||{rules:[],descriptions:[]};for(const name of["tone","emphasis","icon","badge","cue","className","css","bar","scale","band"])
|
|
57
|
+
if(target[name]===undefined&&style[name]!==undefined)target[name]=style[name];target.rules.push(rule.id);if(rule.description&&!target.descriptions.includes(rule.description))target.descriptions.push(rule.description);if(style.band&&!target.descriptions.includes(style.band))target.descriptions.push(style.band);target.details=target.details||rule.details;return target}
|
|
58
|
+
function formatFinish(target){if(!target)return target;if(target.icon===undefined&&target.tone&&!target.badge&&!target.cue&&!target.bar&&!target.scale)target.icon="auto";const name=target.icon==="auto"?FORMAT_TONE_ICONS[target.tone]||"dot":target.icon;target.glyph=name&&name!=="none"?FORMAT_ICONS[name]||name:null;return target}
|
|
59
|
+
Object.assign(DataGrid.prototype,{formatResult(data,details=false){const rules=this.formatCompiled;if(!rules||!rules.length)return null;const cache=details?this.formatDetailCache||=new WeakMap():this.formatCache;const stats=formatStats(this),cached=cache.get(data);if(cached&&cached.version===this.formatVersion)return cached.result;let row=null,cells=null;for(const rule of rules){if(!rule.enabled||rule.test&&!rule.test(data))continue;const style=formatResolve(rule,data,stats);if(style&&(!details||rule.details)){if(rule.target==="row")row=formatMerge(row,rule,style);else for(const id of rule.columns)(cells||=new Map()).set(id,formatMerge(cells.get(id),rule,style))}
|
|
60
|
+
if(rule.stop)break}
|
|
61
|
+
if(cells)for(const target of cells.values())formatFinish(target);const result=row||cells?{row:formatFinish(row),cells:cells||new Map()}:null;cache.set(data,{version:this.formatVersion,result});return result}});const FORMAT_RANK={danger:5,warning:4,info:3,success:2,neutral:1,muted:1};function formatUndo(fx){for(const node of fx.nodes)node.remove();for(const name of fx.props)fx.element.style.removeProperty(name);for(const name of fx.classes)fx.element.classList.remove(name)}
|
|
62
|
+
function formatClear(element){for(const slot of element._cells||[])if(slot&&slot.fx){formatUndo(slot.fx);slot.fx=null}
|
|
63
|
+
if(element._fx){formatUndo(element._fx);element._fx=null}}
|
|
64
|
+
function formatEffects(element){return{element,nodes:[],props:[],classes:[]}}
|
|
65
|
+
function formatDecorate(element,target,fx,before,emphasis){const add=name=>{if(!element.classList.contains(name)){element.classList.add(name);fx.classes.push(name)}};const prop=(target,name,value)=>{target.style.setProperty(name,value);if(target===element)fx.props.push(name)};const node=(tag,className,text)=>{const item=h(tag,className);if(text)setText(item,text);fx.nodes.push(item);return item};if(target.tone&&emphasis){add("cx-fmt-" +(target.bar||target.scale?"text":target.emphasis||emphasis));prop(element,"--cx-fmt",formatToneVar(target.tone))}
|
|
66
|
+
if(target.scale){add("cx-fmt-scale");prop(element,"--cx-fmt-scale",target.scale)}
|
|
67
|
+
if(target.className)for(const name of target.className.split(/\s+/)) if (name) add(name);
|
|
68
|
+
if(target.css)for(const name of Object.keys(target.css))prop(element,name,target.css[name]);if(target.glyph){const icon=node("span","cx-fmt-icon",target.glyph);icon.setAttribute("aria-hidden","true");if(target.tone)prop(icon,"--cx-fmt",formatToneVar(target.tone));element.insertBefore(icon,before&&before.parentNode===element?before:element.firstChild)}
|
|
69
|
+
if(target.bar){add("cx-fmt-barred");if(!target.bar.show)add("cx-fmt-novalue");const bar=node("span","cx-fmt-bar");bar.setAttribute("aria-hidden","true");prop(bar,"--cx-fmt-w",(target.bar.width*100).toFixed(1)+ "%");prop(bar,"--cx-fmt",formatToneVar(target.bar.tone));element.insertBefore(bar,element.firstChild)}
|
|
70
|
+
if(target.badge){const badge=node("span","cx-badge cx-fmt-badge",target.badge),tone=toneOf(target.tone||"neutral");badge.className +=tone.cls;if(tone.host){prop(badge,"--cx-t","var(--cx-tone-"+tone.host+")");prop(badge,"--cx-t-bg","var(--cx-tone-"+tone.host+"-bg)")}
|
|
71
|
+
element.appendChild(badge)}
|
|
72
|
+
if(target.cue)element.appendChild(node("span","cx-fmt-cue",target.cue));if(target.descriptions&&target.descriptions.length)element.appendChild(node("span","cx-sr-only",", "+target.descriptions.join(", ")))}
|
|
73
|
+
const formatCues=target=>({glyph:target.glyph,badge:target.badge,cue:target.cue,tone:target.tone,descriptions:target.descriptions});const FORMAT_PAINT=DataGrid.prototype.paintGridHighlights;DataGrid.prototype.paintGridHighlights=function(element,row){FORMAT_PAINT.call(this,element,row);formatClear(element);const result=!row.status&&this.formatResult&&this.formatResult(row.h);if(!result)return;const slots=element._cells,first=slots[0];if(result.row){const own=result.row;if(own.tone||own.className||own.css){const fx=element._fx=formatEffects(element);setClass(element,element._cls +(own.tone?" cx-fmt-row":"")+(own.className?" "+own.className:""));if(own.tone){element.style.setProperty("--cx-fmt",formatToneVar(own.tone));fx.props.push("--cx-fmt")}
|
|
74
|
+
if(own.css)for(const name of Object.keys(own.css)){element.style.setProperty(name,own.css[name]);fx.props.push(name)}}
|
|
75
|
+
if(first){first.fx=formatEffects(first.cell);first.highlighted=true;formatDecorate(first.cell,formatCues(result.row),first.fx,first.textSpan)}}
|
|
76
|
+
for(let i=0;i<this.cols.length;i++){const target=result.cells.get(this.cols[i].id),slot=slots[i];if(!target||!slot)continue;if(!slot.fx||slot.fx.element!==slot.cell)slot.fx=formatEffects(slot.cell);slot.highlighted=true;formatDecorate(slot.cell,target,slot.fx,slot.textSpan,"fill")}
|
|
77
|
+
if(first&&first.hiddenFields){const details=this.formatResult(row.h,true);if(details)this.formatHiddenFields(first,details)}};const formatFlag=target=>target&&target.details&&(target.tone||target.glyph||target.badge||target.cue);DataGrid.prototype.formatHiddenFields=function(slot,result){const button=slot.hiddenFields,hidden=this.columns.filter(column=>(column.hidden||column.responsiveHidden||column.fitHidden)&&formatFlag(result.cells.get(column.id)));if(!hidden.length)return;let tone=null;for(const column of hidden){const next=result.cells.get(column.id).tone;if(next&&(FORMAT_RANK[next]||3)>(FORMAT_RANK[tone]||0))tone=next}
|
|
78
|
+
const fx=slot.fx||(slot.fx=formatEffects(slot.cell)),effects=formatEffects(button),label=button.getAttribute("aria-label");fx.nodes.push({remove(){formatUndo(effects);button.setAttribute("aria-label",label)}});effects.classes.push("cx-fmt-flag");button.classList.add("cx-fmt-flag");button.style.setProperty("--cx-fmt",tone?formatToneVar(tone):"var(--cx-text-2)");effects.props.push("--cx-fmt");const rules=[...new Set(hidden.flatMap(column=>result.cells.get(column.id).descriptions))].join(", ");button.setAttribute("aria-label",label+". "+fill(this.formatLabels.formatHiddenFlagged,{count:hidden.length,rules}))};const FORMAT_MEASURE=DataGrid.prototype.measureGridColumn;DataGrid.prototype.measureGridColumn=function(column){if(column.formatMinW!==undefined){column.minW=column.formatMinW;delete column.formatMinW}
|
|
79
|
+
const result=FORMAT_MEASURE.call(this,column);if(!this.formatCompiled||!this.formatCompiled.length||!this.fitContext||!this.fitFont)return result;const context=this.fitContext,[font,small,,pad]=this.fitFont,first=column===this.columns.find(item=>!item.hidden);const measure=(text,face)=>{context.font=face;return context.measureText(String(text).slice(0,512)).width};let need=0,floor=0,sampled=0;for(const row of this.rowCache.values()){if(sampled++===200)break;const formats=this.formatResult(row.h),own=formats&&formats.cells.get(column.id),cues=first&&formats&&formats.row;if(!own&&!cues)continue;let width=measure(cellText(row.h.cells[column.id],column,this.fmt)||"—",font),flag=false;for(const target of[own,cues])if(target){if(target.glyph){width +=20;flag=true}
|
|
80
|
+
if(target.badge){width +=measure(target.badge,small)+ 20;flag=true}
|
|
81
|
+
if(target.cue){width +=measure(target.cue,small)+ 6;flag=true}
|
|
82
|
+
if(target.bar)width +=30}
|
|
83
|
+
width=Math.ceil(width+2*pad+4);need=Math.max(need,width);if(flag)floor=Math.max(floor,width)}
|
|
84
|
+
if(floor){column.formatMinW=column.minW;column.minW=Math.max(column.minW||0,Math.min(floor,420))}
|
|
85
|
+
return need>result.width||floor>result.width?Object.freeze({...result,width:Math.max(result.width,Math.min(Math.max(need,floor),420))}):result};DataGrid.prototype.formatRefit=function(){this.fitKey=null;if(this.sized&&this.grid.clientWidth)this.applyWidths()};function formatDrawer(grid,dialog){let effects=[];const paint=()=>{effects.forEach(formatUndo);effects=[];const head=dialog.querySelector(".cx-drawer-h"),title=head&&head.querySelector("h4"),data=dialog._cxRecord;if(!dialog.isConnected||!title||!data||!grid.formatCompiled||!grid.formatCompiled.length)return;const result=grid.formatResult(data,true);if(!result)return;if(result.row){const fx=formatEffects(head);effects.push(fx);formatDecorate(head,{tone:result.row.tone,descriptions:result.row.descriptions,glyph:result.row.glyph},fx,title,"accent")}
|
|
86
|
+
for(const field of grid.drawerFields(dialog)){const target=field.column&&result.cells.get(field.column.id);if(!target||!target.details)continue;const fx=formatEffects(field.value);effects.push(fx);formatDecorate(field.value,target,fx,field.value.firstChild,"fill")}};dialog.addEventListener("cx-drawer-paint",paint);dialog.addEventListener("close",()=>{effects.forEach(formatUndo);effects=[]},{once:true})}
|
|
87
|
+
DataGrid.drawerHook(20,function(dialog){formatDrawer(this,dialog)});const FORMAT_EDITOR=definePart(DataGrid,"format-editor","formatting rule editor",()=>({DataGrid,h,setText,fill,gridButton,gridOption,gridDateInputMoment,gridInputLabel,gridIcon,gridDecorateButton,announceMenu,formatCompile,formatToneVar,formatError,FORMAT_ICONS,FORMAT_TONE_ICONS,FORMAT_OPERATORS,svgIcon}));function formatPick(labels){const own={};for(const name of Object.keys(FORMAT_LABELS))own[name]=Object.hasOwn(labels,name)?labels[name]:FORMAT_LABELS[name];return own}
|
|
88
|
+
Object.assign(DataGrid.prototype,{validateFormatRules(rules){formatCompile(rules,this)},startFormatting(){if(this.formatStarted||this.destroyed)return;this.formatStarted=true;this.formatVersion=1;this.formatCache=new WeakMap();this.formatLabels=formatPick(this.labels);try{this.formatOptions=formatOptions(this.view.formatting);this.formatCompiled=formatCompile(this.view.formatRules||[],this)}
|
|
89
|
+
catch(error){this.formatOptions=this.formatOptions||{};this.formatCompiled=[];this.emit("error",{error})}
|
|
90
|
+
this.formatRules=gridFreeze(JSON.parse(JSON.stringify(this.formatCompiled.length?this.view.formatRules:[])));this.formatEdited=false;this.formatLegendShown=!!this.formatOptions.legend;if(this.formatOptions.editor&&this.overflow){const entry=gridButton(this.formatLabels.formatRules,()=>this.openFormatRules().catch(error=>this.emit("error",{error})));entry.classList.add("cx-grid-format-rules");entry.setAttribute("aria-haspopup","dialog");this.overflow.panel.insertBefore(entry,this.overflow.panel.querySelector(".cx-grid-details, .cx-grid-print"))}
|
|
91
|
+
this.syncFormatLegend();this.syncFormatClock();this.formatRefit();this.version++;this.scheduleRender()},syncFormatClock(){const relative=this.formatCompiled.some(rule=>rule.relative);if(relative&&!this.formatClock){this.formatClock=setInterval(()=>{if(this.destroyed)return;this.formatVersion++;this.version++;this.scheduleRender()},60000);this.lifetime.signal.addEventListener("abort",()=>clearInterval(this.formatClock),{once:true})}else if(!relative&&this.formatClock){clearInterval(this.formatClock);this.formatClock=null}},setFormatRules(rules){try{if(this.destroyed)throw new DOMException("Grid was destroyed.","AbortError");this.startFormatting();const json=JSON.stringify(rules),compiled=formatCompile(JSON.parse(json??"null"),this);if(json!==JSON.stringify(this.formatRules)){this.formatRules=gridFreeze(JSON.parse(json));this.formatCompiled=compiled;this.formatEdited=json!==JSON.stringify(this.view.formatRules||[]);this.formatVersion++;this.formatStatsKey=undefined;this.version++;this.scheduleRender();this.syncFormatLegend();this.syncFormatClock();this.formatRefit();this.stateChanged();this.emit("format",{rules:this.getFormatRules()})}
|
|
92
|
+
return Promise.resolve(this.getFormatRules())}catch(error){return Promise.reject(error)}},openFormatRules(){this.startFormatting();return loadPart(FORMAT_EDITOR).then(()=>{if(!this.destroyed)this.showFormatEditor()})},setFormatLegend(shown){this.formatLegendShown=!!shown;this.syncFormatLegend()},syncFormatLegend(){const rules=this.formatLegendShown?this.formatCompiled.filter(rule=>rule.enabled&&rule.label):[];if(!rules.length){this.formatLegend?.remove();this.formatLegend=null;return}
|
|
93
|
+
const legend=this.formatLegend||=h("ul","cx-format-legend");legend.setAttribute("aria-label",this.formatLabels.formatLegend);legend.replaceChildren();const key=(text,style,description)=>{const item=h("li","cx-format-key"),swatch=h("span","cx-format-swatch"),label=h("span");swatch.setAttribute("aria-hidden","true");setText(label,text);const target=formatFinish({tone:style.tone,icon:style.icon,badge:style.badge,cue:style.cue,emphasis:style.emphasis}),fx=formatEffects(swatch);if(style.scale){swatch.classList.add("cx-fmt-gradient");swatch.style.setProperty("--cx-fmt-scale","linear-gradient(90deg,"+style.scale.stops.map(stop=>formatToneVar(stop.tone)).join(",")+ ")")}
|
|
94
|
+
else if(style.bar)formatDecorate(swatch,{bar:{width:.7,tone:style.bar.tone||style.tone||"info"}},fx,null);else if(target.badge&&!target.glyph){swatch.classList.add("cx-format-badge");formatDecorate(swatch,{badge:target.badge,tone:target.tone},fx,null)}
|
|
95
|
+
else formatDecorate(swatch,{tone:target.tone,glyph:target.glyph,emphasis:target.emphasis},fx,null,"fill");if(description&&description!==text)item.title=description;item.append(swatch,label);legend.appendChild(item)};for(const rule of rules){if(rule.style.bands)rule.style.bands.forEach((band,index)=>key(rule.label+": " +(band.label||(band.upperBound===undefined?
|
|
96
|
+
this.formatLabels.formatAbove+" "+numberFormat(this.lang).format(rule.style.bands[index-1]?.upperBound??0):
|
|
97
|
+
this.formatLabels.formatUpTo+" "+numberFormat(this.lang).format(band.upperBound))),{tone:band.tone,icon:band.icon??rule.style.icon}));else key(rule.label,rule.style,rule.description)}
|
|
98
|
+
if(!legend.isConnected)this.host.insertBefore(legend,this.notice)}});function formatExported(target){if(!target)return undefined;const out={rules:target.rules.slice()};for(const name of["tone","badge","cue","band"])if(target[name]!==undefined)out[name]=target[name];if(target.glyph)out.icon=target.glyph;if(target.bar)out.bar={fraction:Math.round(target.bar.width*1000)/ 1000, tone: target.bar.tone };
|
|
99
|
+
if(target.scale)out.color=target.scale;if(target.descriptions.length)out.description=target.descriptions.join(", ");return out}
|
|
100
|
+
GRID_REPORTING.formatRecord=(grid,row,record)=>{const result=grid.formatResult&&grid.formatResult(row);if(!result)return record;const cells={};for(const[id,cell]of Object.entries(record.cells)){const format=formatExported(result.cells.get(id));cells[id]=format?{...cell,format}:cell}
|
|
101
|
+
return gridFreeze({...record,cells,...(result.row?{format:formatExported(result.row)}:{})})};installed.add(context.DataGrid.prototype)}};(global.CanopyXParts||=[]).push(["formatting",part])})(typeof window!=="undefined"?window:globalThis);
|