@aceshooting/lyra-ui 11.2.0 → 12.1.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.
Files changed (222) hide show
  1. package/CHANGELOG.md +407 -0
  2. package/README.md +11 -8
  3. package/custom-elements.json +1 -1
  4. package/dist/all.d.ts +1 -0
  5. package/dist/all.js +1 -1
  6. package/dist/autoloader.js +1 -1
  7. package/dist/components/agent-tools/tool-param-form/tool-param-form.class.d.ts +0 -1
  8. package/dist/components/agent-tools/tool-param-form/tool-param-form.class.js +1 -1
  9. package/dist/components/charts/chart/chart.class.d.ts +13 -1
  10. package/dist/components/charts/chart/histogram-bin.d.ts +0 -1
  11. package/dist/components/charts/chart/histogram-bin.js +1 -1
  12. package/dist/components/charts/chart/lite-chart.class.d.ts +22 -9
  13. package/dist/components/charts/chart/lite-chart.class.js +6 -6
  14. package/dist/components/conversation/code-block/code-block-shared.d.ts +4 -54
  15. package/dist/components/conversation/code-block/code-block-shared.js +1 -1
  16. package/dist/components/conversation/code-block/shiki-types.d.ts +2 -1
  17. package/dist/components/conversation/generation-metrics/generation-metrics.class.js +1 -1
  18. package/dist/components/conversation/model-select/model-select.class.d.ts +0 -3
  19. package/dist/components/conversation/model-select/model-select.class.js +1 -1
  20. package/dist/components/conversation/selection-toolbar/selection-toolbar.class.js +1 -1
  21. package/dist/components/conversation/voice-picker/voice-picker.class.d.ts +0 -1
  22. package/dist/components/conversation/voice-picker/voice-picker.class.js +1 -1
  23. package/dist/components/data/data-grid/data-grid.class.js +1 -1
  24. package/dist/components/data/funnel/funnel.class.d.ts +92 -0
  25. package/dist/components/data/funnel/funnel.class.js +25 -0
  26. package/dist/components/data/funnel/funnel.d.ts +1 -0
  27. package/dist/components/data/funnel/funnel.js +1 -0
  28. package/dist/components/data/funnel/funnel.styles.d.ts +1 -0
  29. package/dist/components/data/funnel/funnel.styles.js +127 -0
  30. package/dist/components/data/graph-query-builder/graph-query-builder.class.d.ts +0 -1
  31. package/dist/components/data/graph-query-builder/graph-query-builder.class.js +1 -1
  32. package/dist/components/data/heatmap/heatmap.class.d.ts +199 -14
  33. package/dist/components/data/heatmap/heatmap.class.js +30 -22
  34. package/dist/components/data/heatmap/heatmap.styles.js +36 -0
  35. package/dist/components/data/index.d.ts +1 -0
  36. package/dist/components/data/index.js +1 -1
  37. package/dist/components/data/table/table.class.d.ts +18 -4
  38. package/dist/components/data/table/table.class.js +2 -2
  39. package/dist/components/data/table/table.styles.js +2 -1
  40. package/dist/components/data/timeline/timeline-cluster.d.ts +1 -0
  41. package/dist/components/data/timeline/timeline-cluster.js +1 -0
  42. package/dist/components/data/timeline/timeline-item.class.d.ts +17 -2
  43. package/dist/components/data/timeline/timeline-item.class.js +13 -2
  44. package/dist/components/data/timeline/timeline-item.styles.js +65 -0
  45. package/dist/components/data/timeline/timeline.class.d.ts +83 -27
  46. package/dist/components/data/timeline/timeline.class.js +3 -3
  47. package/dist/components/data/timeline/timeline.d.ts +1 -0
  48. package/dist/components/data/timeline/timeline.js +1 -1
  49. package/dist/components/data/timeline/timeline.styles.js +14 -0
  50. package/dist/components/forms/button/button.class.js +1 -1
  51. package/dist/components/forms/checkbox/checkbox.class.d.ts +0 -1
  52. package/dist/components/forms/checkbox/checkbox.class.js +1 -1
  53. package/dist/components/forms/checkbox-group/checkbox-group.class.d.ts +0 -1
  54. package/dist/components/forms/checkbox-group/checkbox-group.class.js +1 -1
  55. package/dist/components/forms/code-editor/code-editor.class.js +1 -1
  56. package/dist/components/forms/color-picker/color-picker.class.js +1 -1
  57. package/dist/components/forms/combobox/combobox.class.d.ts +7 -1
  58. package/dist/components/forms/combobox/combobox.class.js +3 -2
  59. package/dist/components/forms/combobox/combobox.styles.js +1 -1
  60. package/dist/components/forms/date-picker/date-input.class.d.ts +23 -0
  61. package/dist/components/forms/date-picker/date-input.class.js +1 -1
  62. package/dist/components/forms/emoji-picker/emoji-data-loader.d.ts +0 -1
  63. package/dist/components/forms/input/native-time-input.class.js +1 -1
  64. package/dist/components/forms/input/time-input-shared.d.ts +2 -1
  65. package/dist/components/forms/input/time-input.class.js +1 -1
  66. package/dist/components/forms/locale-picker/locale-picker.class.d.ts +0 -1
  67. package/dist/components/forms/locale-picker/locale-picker.class.js +1 -1
  68. package/dist/components/forms/radio/radio-button.styles.d.ts +0 -1
  69. package/dist/components/forms/radio/radio-button.styles.js +1 -1
  70. package/dist/components/forms/radio/radio-group.class.d.ts +0 -1
  71. package/dist/components/forms/radio/radio-group.class.js +1 -1
  72. package/dist/components/forms/radio/radio.class.d.ts +0 -1
  73. package/dist/components/forms/radio/radio.class.js +1 -1
  74. package/dist/components/forms/rubric-form/rubric-form.class.d.ts +0 -1
  75. package/dist/components/forms/rubric-form/rubric-form.class.js +1 -1
  76. package/dist/components/forms/select/select.class.d.ts +0 -1
  77. package/dist/components/forms/select/select.class.js +1 -1
  78. package/dist/components/forms/slider/slider.class.d.ts +0 -1
  79. package/dist/components/forms/slider/slider.class.js +1 -1
  80. package/dist/components/forms/switch/switch.class.d.ts +0 -1
  81. package/dist/components/forms/switch/switch.class.js +1 -1
  82. package/dist/components/forms/time-range/time-range.class.d.ts +0 -1
  83. package/dist/components/forms/time-range/time-range.class.js +1 -1
  84. package/dist/components/forms/token-input/token-input.class.d.ts +0 -1
  85. package/dist/components/forms/token-input/token-input.class.js +1 -1
  86. package/dist/components/layout/app-rail/app-rail-item.class.d.ts +16 -0
  87. package/dist/components/layout/app-rail/app-rail-item.class.js +4 -4
  88. package/dist/components/layout/dashboard-grid/layout-internal.d.ts +0 -1
  89. package/dist/components/layout/dashboard-grid/layout-internal.js +1 -1
  90. package/dist/components/layout/details/accordion-owner.d.ts +1 -1
  91. package/dist/components/layout/filter-bar/filter-bar.class.d.ts +32 -3
  92. package/dist/components/layout/filter-bar/filter-bar.class.js +2 -1
  93. package/dist/components/layout/menu/menu-item.class.js +1 -1
  94. package/dist/components/layout/virtual-list/virtual-list.class.js +1 -1
  95. package/dist/components/layout/widget/widget.class.d.ts +16 -0
  96. package/dist/components/layout/widget/widget.class.js +2 -2
  97. package/dist/components/lr-funnel.d.ts +1 -0
  98. package/dist/components/lr-funnel.js +1 -0
  99. package/dist/components/media/av-player/av-metadata.d.ts +0 -2
  100. package/dist/components/media/av-player/av-metadata.js +1 -1
  101. package/dist/components/media/file-input/file-input.class.d.ts +0 -1
  102. package/dist/components/media/file-input/file-input.class.js +1 -1
  103. package/dist/components/media/flag/flag-peer-bulk-standard.d.ts +25 -0
  104. package/dist/components/media/flag/flag-peer-bulk-standard.js +1 -0
  105. package/dist/components/media/flag/flag.class.d.ts +8 -0
  106. package/dist/components/media/flag/flag.class.js +1 -1
  107. package/dist/components/media/map/map-loader.d.ts +4 -1
  108. package/dist/components/media/map/map.class.d.ts +242 -23
  109. package/dist/components/media/map/map.class.js +2 -2
  110. package/dist/components/overlays/chip/chip-group.class.d.ts +16 -0
  111. package/dist/components/overlays/chip/chip-group.class.js +10 -3
  112. package/dist/components/overlays/overlay/popover.class.d.ts +7 -1
  113. package/dist/components/overlays/overlay/popover.class.js +1 -1
  114. package/dist/components/overlays/overlay/tooltip.class.d.ts +1 -0
  115. package/dist/components/overlays/overlay/tooltip.class.js +1 -1
  116. package/dist/components/overlays/progress/progress-shared.d.ts +0 -2
  117. package/dist/components/overlays/progress/progress-shared.js +1 -1
  118. package/dist/components/overlays/rating/rating.class.js +1 -1
  119. package/dist/components/retrieval/graph/graph-canvas.d.ts +10 -9
  120. package/dist/components/retrieval/mind-map/mind-map-layout.d.ts +0 -4
  121. package/dist/components/retrieval/mind-map/mind-map-layout.js +1 -1
  122. package/dist/components/utility/format/format-options.d.ts +0 -2
  123. package/dist/components/utility/format/format-options.js +1 -1
  124. package/dist/components/utility/icon/icon-resource.d.ts +0 -2
  125. package/dist/components/utility/icon/icon-resource.js +1 -1
  126. package/dist/components/viewers/archive-viewer/zip-resource-guard.d.ts +2 -1
  127. package/dist/components/viewers/docx-viewer/docx-resource-guard.d.ts +0 -3
  128. package/dist/components/viewers/docx-viewer/docx-resource-guard.js +1 -1
  129. package/dist/components/viewers/ebook-viewer/ebook-loader.d.ts +2 -1
  130. package/dist/components/viewers/ebook-viewer/epub-resource-guard.d.ts +0 -3
  131. package/dist/components/viewers/ebook-viewer/epub-resource-guard.js +1 -1
  132. package/dist/components/viewers/pdf-viewer/pdf-loader.d.ts +2 -2
  133. package/dist/components/viewers/pdf-viewer/pdf-viewer.class.js +2 -2
  134. package/dist/components/viewers/pptx-viewer/pptx-resource-guard.d.ts +0 -2
  135. package/dist/components/viewers/pptx-viewer/pptx-resource-guard.js +1 -1
  136. package/dist/components/viewers/spreadsheet-viewer/xlsx-resource-guard.d.ts +0 -5
  137. package/dist/components/viewers/spreadsheet-viewer/xlsx-resource-guard.js +1 -1
  138. package/dist/custom-elements-jsx.d.ts +16 -8
  139. package/dist/events.d.ts +15 -0
  140. package/dist/internal/accessibility-visibility.d.ts +1 -1
  141. package/dist/internal/anchor-target.d.ts +1 -3
  142. package/dist/internal/anchor-target.js +2 -2
  143. package/dist/internal/anchored-overlay-runtime.d.ts +7 -0
  144. package/dist/internal/anchored-overlay-runtime.js +1 -0
  145. package/dist/internal/announcement-text.d.ts +1 -1
  146. package/dist/internal/ansi.d.ts +0 -4
  147. package/dist/internal/ansi.js +1 -1
  148. package/dist/internal/aria-ownership.d.ts +2 -1
  149. package/dist/internal/autoloader-manifest.js +1 -1
  150. package/dist/internal/autoloader-tags.d.ts +1 -1
  151. package/dist/internal/autoloader-tags.js +1 -1
  152. package/dist/internal/default-strings.generated.d.ts +1 -0
  153. package/dist/internal/default-strings.generated.js +1 -1
  154. package/dist/internal/focus-navigation.d.ts +3 -2
  155. package/dist/internal/form-associated.d.ts +0 -1
  156. package/dist/internal/form-associated.js +1 -1
  157. package/dist/internal/form-control-labels.d.ts +0 -3
  158. package/dist/internal/form-control-labels.js +1 -1
  159. package/dist/internal/localization-runtime.js +1 -1
  160. package/dist/internal/localization-types.d.ts +1 -1
  161. package/dist/internal/localization.js +1 -1
  162. package/dist/internal/lyra-element.d.ts +7 -14
  163. package/dist/internal/lyra-element.js +1 -1
  164. package/dist/internal/nonmodal-overlay-manager.d.ts +9 -0
  165. package/dist/internal/nonmodal-overlay-manager.js +1 -0
  166. package/dist/internal/overlay-manager.d.ts +4 -20
  167. package/dist/internal/overlay-manager.js +1 -1
  168. package/dist/internal/overlay-stack.d.ts +66 -0
  169. package/dist/internal/overlay-stack.js +1 -0
  170. package/dist/internal/package-metadata.d.ts +1 -1
  171. package/dist/internal/package-metadata.js +1 -1
  172. package/dist/internal/positioner.js +1 -1
  173. package/dist/internal/root-registration-allowlist.d.ts +1 -1
  174. package/dist/internal/root-registration-allowlist.js +1 -1
  175. package/dist/internal/text-quote.d.ts +1 -1
  176. package/dist/lyra.d.ts +5 -4
  177. package/dist/lyra.js +1 -1
  178. package/dist/ssr/all.d.ts +1 -0
  179. package/dist/ssr/all.js +1 -1
  180. package/dist/ssr.d.ts +4 -4
  181. package/dist/styles/tokens-root.css +488 -0
  182. package/dist/svelte.d.ts +17 -8
  183. package/dist/translations/ar.js +1 -1
  184. package/dist/translations/de.js +1 -1
  185. package/dist/translations/es.js +1 -1
  186. package/dist/translations/fa.js +1 -1
  187. package/dist/translations/fr.js +1 -1
  188. package/dist/translations/he.js +1 -1
  189. package/dist/translations/ja.js +1 -1
  190. package/dist/translations/pt-BR.js +1 -1
  191. package/dist/translations/ru.js +1 -1
  192. package/dist/translations/zh-CN.js +1 -1
  193. package/dist/utilities/localization.js +1 -1
  194. package/dist/vue.d.ts +16 -8
  195. package/llms/components/lr-app-rail-item.md +8 -1
  196. package/llms/components/lr-app-rail.md +6 -0
  197. package/llms/components/lr-chip-group.md +7 -0
  198. package/llms/components/lr-chip.md +7 -0
  199. package/llms/components/lr-combobox.md +42 -0
  200. package/llms/components/lr-date-input.md +19 -1
  201. package/llms/components/lr-date-picker.md +19 -1
  202. package/llms/components/lr-filter-bar.md +50 -1
  203. package/llms/components/lr-flag.md +14 -0
  204. package/llms/components/lr-funnel.md +122 -0
  205. package/llms/components/lr-heatmap.md +55 -8
  206. package/llms/components/lr-lite-chart.md +20 -8
  207. package/llms/components/lr-map.md +109 -22
  208. package/llms/components/lr-option.md +42 -0
  209. package/llms/components/lr-popover.md +39 -0
  210. package/llms/components/lr-table.md +7 -3
  211. package/llms/components/lr-timeline-item.md +49 -24
  212. package/llms/components/lr-timeline.md +49 -24
  213. package/llms/components/lr-widget.md +7 -1
  214. package/llms/index.md +3 -2
  215. package/llms/peers.md +1 -1
  216. package/llms/shared.md +110 -1
  217. package/llms-full.txt +681 -67
  218. package/llms.txt +1 -1
  219. package/package.json +26 -8
  220. package/vscode-css-data.json +63 -0
  221. package/vscode-html-data.json +108 -23
  222. package/web-types.json +32738 -310
@@ -0,0 +1,127 @@
1
+ import{css}from"lit";const styles=css`
2
+ :host {
3
+ display: block;
4
+ font-family: var(--lr-font);
5
+ color: var(--lr-color-text);
6
+ }
7
+
8
+ [part='base'] {
9
+ min-inline-size: 0;
10
+ container-type: inline-size;
11
+ contain-intrinsic-inline-size: var(--lr-size-16rem);
12
+ }
13
+
14
+ [part='stages'] {
15
+ list-style: none;
16
+ margin: 0;
17
+ padding: 0;
18
+ display: flex;
19
+ flex-direction: column;
20
+ gap: var(--lr-space-s);
21
+ }
22
+
23
+ [part='stage'] {
24
+ display: flex;
25
+ flex-direction: column;
26
+ gap: var(--lr-space-2xs);
27
+ min-inline-size: 0;
28
+ }
29
+
30
+ [part='dropoff'] {
31
+ font-size: var(--lr-font-size-xs);
32
+ color: var(--lr-color-text-quiet);
33
+ align-self: flex-start;
34
+ }
35
+
36
+ [part='stage-header'] {
37
+ display: flex;
38
+ flex-wrap: wrap;
39
+ align-items: baseline;
40
+ gap: var(--lr-space-2xs) var(--lr-space-xs);
41
+ min-inline-size: 0;
42
+ }
43
+
44
+ [part='stage-label'] {
45
+ flex: 1 1 auto;
46
+ min-inline-size: 0;
47
+ font-size: var(--lr-font-size-sm);
48
+ overflow-wrap: anywhere;
49
+ text-align: start;
50
+ }
51
+
52
+ [part='stage-value'] {
53
+ font-size: var(--lr-font-size-sm);
54
+ font-weight: var(--lr-font-weight-semibold);
55
+ font-variant-numeric: tabular-nums;
56
+ }
57
+
58
+ [part='stage-share'] {
59
+ font-size: var(--lr-font-size-sm);
60
+ color: var(--lr-color-text-quiet);
61
+ font-variant-numeric: tabular-nums;
62
+ }
63
+
64
+ [part='comparison-value'] {
65
+ flex-basis: 100%;
66
+ font-size: var(--lr-font-size-xs);
67
+ color: var(--lr-color-text-quiet);
68
+ text-align: start;
69
+ }
70
+
71
+ [part='track'] {
72
+ position: relative;
73
+ display: block;
74
+ inline-size: 100%;
75
+ block-size: var(--lr-funnel-bar-size, var(--lr-size-1-5rem));
76
+ background: var(--lr-funnel-track-color, var(--lr-color-surface-raised));
77
+ border-radius: var(--lr-radius-xs);
78
+ }
79
+
80
+ [part~='bar'],
81
+ [part='comparison-bar'] {
82
+ position: absolute;
83
+ inset-inline-start: 0;
84
+ border-radius: inherit;
85
+ transition: inline-size var(--lr-transition-base);
86
+ }
87
+
88
+ [part='comparison-bar'] {
89
+ inset-block: 0;
90
+ border: var(--lr-size-1px) dashed
91
+ var(--lr-funnel-comparison-color, var(--lr-color-border-strong));
92
+ background: none;
93
+ }
94
+
95
+ [part~='bar'] {
96
+ inset-block: 0;
97
+ background: var(--lr-funnel-bar-color, var(--lr-color-brand));
98
+ }
99
+
100
+ [part='comparison-bar'] ~ [part~='bar'] {
101
+ inset-block: var(--lr-size-0-25rem);
102
+ }
103
+
104
+ [part~='bar-overflow'] {
105
+ border-inline-end: var(--lr-size-3px) solid var(--lr-color-text);
106
+ }
107
+
108
+ [part='empty'] {
109
+ margin: 0;
110
+ font-size: var(--lr-font-size-sm);
111
+ color: var(--lr-color-text-quiet);
112
+ text-align: start;
113
+ }
114
+
115
+ @container (max-inline-size: 18rem) {
116
+ [part='stage-label'] {
117
+ flex-basis: 100%;
118
+ }
119
+ }
120
+
121
+ @media (prefers-reduced-motion: reduce) {
122
+ [part~='bar'],
123
+ [part='comparison-bar'] {
124
+ transition: none;
125
+ }
126
+ }
127
+ `;export{styles};
@@ -1,7 +1,6 @@
1
1
  import type { LyraEventDetailSnapshot } from '../../../internal/lyra-element.js';
2
2
  import { type TemplateResult, type PropertyValues } from 'lit';
3
3
  import { LyraElement } from '../../../internal/lyra-element.js';
4
- import '../../../internal/form-control-labels.js';
5
4
  import { type FormOwnerValue } from '../../../internal/form-associated.js';
6
5
  /** Traversal direction relative to the matched node(s): `'out'` (outgoing edges), `'in'`
7
6
  * (incoming edges), or `'both'`. */
@@ -1,4 +1,4 @@
1
- var __decorate=function(decorators,target,key,desc){var c=arguments.length,r=c<3?target:desc===null?desc=Object.getOwnPropertyDescriptor(target,key):desc,d;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")r=Reflect.decorate(decorators,target,key,desc);else for(var i=decorators.length-1;i>=0;i--)(d=decorators[i])&&(r=(c<3?d(r):c>3?d(target,key,r):d(target,key))||r);return c>3&&r&&Object.defineProperty(target,key,r),r};import{html,nothing}from"lit";import{property,state}from"lit/decorators.js";import{LyraElement}from"../../../internal/lyra-element.js";import"../../../internal/form-control-labels.js";import{closeIcon}from"../../../internal/icons.js";import{AnchoredValidityController,VALIDITY_ANCHOR}from"../../../internal/anchored-validity.js";import{syncValidityStates}from"../../../internal/custom-states.js";import{finiteInteger}from"../../../internal/numbers.js";import{getNumberFormat}from"../../../internal/intl-cache.js";import{nextId}from"../../../internal/a11y.js";import{deepActiveElementIn}from"../../../internal/active-element.js";import{styles}from"./graph-query-builder.styles.js";import{attachInternalsSafely}from"../../../internal/element-internals.js";import{getFormOwner,installCustomErrorProperty,isBarredFromValidation,setFormOwner}from"../../../internal/form-associated.js";import{installInvalidEventAlias}from"../../../internal/invalid-event-alias.js";import{LYRA_DEFAULT_fieldRequired,LYRA_DEFAULT_graphQueryBuilderLabel,LYRA_DEFAULT_graphQueryDeleteWithContext,LYRA_DEFAULT_graphQueryDirectionLabel,LYRA_DEFAULT_graphQueryEndLabel,LYRA_DEFAULT_graphQueryHopRangeInvalid,LYRA_DEFAULT_graphQueryLoadWithContext,LYRA_DEFAULT_graphQueryMaxHopsLabel,LYRA_DEFAULT_graphQueryMinHopsLabel,LYRA_DEFAULT_graphQueryNodeTypeLabel,LYRA_DEFAULT_graphQueryRelationshipTypeLabel,LYRA_DEFAULT_graphQueryRun,LYRA_DEFAULT_graphQuerySaveButton,LYRA_DEFAULT_graphQuerySaveNameLabel,LYRA_DEFAULT_graphQuerySavedQueriesLabel,LYRA_DEFAULT_graphQueryStartLabel,LYRA_DEFAULT_neighborDirectionBoth,LYRA_DEFAULT_neighborDirectionIn,LYRA_DEFAULT_neighborDirectionOut,LYRA_DEFAULT_noData,LYRA_DEFAULT_select}from"../../../internal/default-strings.generated.js";const MAX_TYPES=500,MAX_SAVED_QUERIES=200,MAX_TEXT=256,EMPTY_STRINGS=Object.freeze([]),EMPTY_VALUE=Object.freeze({startId:"",endId:"",relationshipTypes:EMPTY_STRINGS,nodeTypes:EMPTY_STRINGS,direction:"both",minHops:1,maxHops:1}),EMPTY_OPTIONS=Object.freeze([]),EMPTY_SAVED=Object.freeze([]);function ownValue(record,key){try{const descriptor=Object.getOwnPropertyDescriptor(record,key);return descriptor&&"value"in descriptor?descriptor.value:void 0}catch{return}}function boundedString(value){return typeof value=="string"?value.slice(0,MAX_TEXT):""}function stringArray(value){return Array.isArray(value)?Object.freeze([...new Set(value.slice(0,MAX_TYPES).filter(entry=>typeof entry=="string").map(entry=>boundedString(entry)).filter(Boolean))]):EMPTY_STRINGS}function normalizeGraphQuery(value){const record=value!==null&&typeof value=="object"&&!Array.isArray(value)?value:EMPTY_VALUE,direction=ownValue(record,"direction");return Object.freeze({startId:boundedString(ownValue(record,"startId")),endId:boundedString(ownValue(record,"endId")),relationshipTypes:stringArray(ownValue(record,"relationshipTypes")),nodeTypes:stringArray(ownValue(record,"nodeTypes")),direction:direction==="out"||direction==="in"||direction==="both"?direction:"both",minHops:finiteInteger(typeof ownValue(record,"minHops")=="number"?ownValue(record,"minHops"):EMPTY_VALUE.minHops,EMPTY_VALUE.minHops,1,20),maxHops:finiteInteger(typeof ownValue(record,"maxHops")=="number"?ownValue(record,"maxHops"):EMPTY_VALUE.maxHops,EMPTY_VALUE.maxHops,1,20)})}function normalizeTypeOptions(value){if(!Array.isArray(value))return EMPTY_OPTIONS;const values=new Set,result=[];for(const candidate of value.slice(0,MAX_TYPES)){if(candidate===null||typeof candidate!="object"||Array.isArray(candidate))continue;const optionValue=boundedString(ownValue(candidate,"value"));if(optionValue.trim().length===0||values.has(optionValue))continue;values.add(optionValue);const label=boundedString(ownValue(candidate,"label"));result.push(Object.freeze({value:optionValue,...label?{label}:{}}))}return Object.freeze(result)}function normalizeSavedQueries(value){if(!Array.isArray(value))return EMPTY_SAVED;const ids=new Set,result=[];for(const candidate of value.slice(0,MAX_SAVED_QUERIES)){if(candidate===null||typeof candidate!="object"||Array.isArray(candidate))continue;const id=boundedString(ownValue(candidate,"id"));id.trim().length===0||ids.has(id)||(ids.add(id),result.push(Object.freeze({id,name:boundedString(ownValue(candidate,"name")),query:normalizeGraphQuery(ownValue(candidate,"query"))})))}return Object.freeze(result)}class LyraGraphQueryBuilder extends LyraElement{static{this.defaultStrings={...super.defaultStrings,fieldRequired:LYRA_DEFAULT_fieldRequired,graphQueryBuilderLabel:LYRA_DEFAULT_graphQueryBuilderLabel,graphQueryDeleteWithContext:LYRA_DEFAULT_graphQueryDeleteWithContext,graphQueryDirectionLabel:LYRA_DEFAULT_graphQueryDirectionLabel,graphQueryEndLabel:LYRA_DEFAULT_graphQueryEndLabel,graphQueryHopRangeInvalid:LYRA_DEFAULT_graphQueryHopRangeInvalid,graphQueryLoadWithContext:LYRA_DEFAULT_graphQueryLoadWithContext,graphQueryMaxHopsLabel:LYRA_DEFAULT_graphQueryMaxHopsLabel,graphQueryMinHopsLabel:LYRA_DEFAULT_graphQueryMinHopsLabel,graphQueryNodeTypeLabel:LYRA_DEFAULT_graphQueryNodeTypeLabel,graphQueryRelationshipTypeLabel:LYRA_DEFAULT_graphQueryRelationshipTypeLabel,graphQueryRun:LYRA_DEFAULT_graphQueryRun,graphQuerySaveButton:LYRA_DEFAULT_graphQuerySaveButton,graphQuerySaveNameLabel:LYRA_DEFAULT_graphQuerySaveNameLabel,graphQuerySavedQueriesLabel:LYRA_DEFAULT_graphQuerySavedQueriesLabel,graphQueryStartLabel:LYRA_DEFAULT_graphQueryStartLabel,neighborDirectionBoth:LYRA_DEFAULT_neighborDirectionBoth,neighborDirectionIn:LYRA_DEFAULT_neighborDirectionIn,neighborDirectionOut:LYRA_DEFAULT_neighborDirectionOut,noData:LYRA_DEFAULT_noData,select:LYRA_DEFAULT_select}}static{this.formAssociated=!0}static{this.styles=[LyraElement.styles,styles]}static{this.immutableEventDetails=Object.freeze(["lr-input","lr-before-query-run","lr-query-run","lr-before-query-save","lr-query-save","lr-before-query-load","lr-query-load"])}static{this.properties={customError:{attribute:"custom-error",reflect:!0,noAccessor:!0},name:{reflect:!0,noAccessor:!0},value:{attribute:!1,noAccessor:!0},disabled:{type:Boolean,reflect:!0,noAccessor:!0}}}get relationshipTypeOptions(){return this._relationshipTypeOptions}set relationshipTypeOptions(value){const previous=this._relationshipTypeOptions;this._relationshipTypeOptions=normalizeTypeOptions(value),this.requestUpdate("relationshipTypeOptions",previous)}get nodeTypeOptions(){return this._nodeTypeOptions}set nodeTypeOptions(value){const previous=this._nodeTypeOptions;this._nodeTypeOptions=normalizeTypeOptions(value),this.requestUpdate("nodeTypeOptions",previous)}get savedQueries(){return this._savedQueries}set savedQueries(value){const previous=this._savedQueries;this._savedQueries=normalizeSavedQueries(value),this.requestUpdate("savedQueries",previous)}constructor(){super(),this._relationshipTypeOptions=EMPTY_OPTIONS,this._nodeTypeOptions=EMPTY_OPTIONS,this._savedQueries=EMPTY_SAVED,this.hopLimit=6,this.label="",this.hint="",this.errorText="",this._errors={},this.touchedFields=new Set,this.saveName="",this.hasHintSlot=!1,this.hasErrorSlot=!1,this.labelId=nextId("graph-query-builder-label"),this.hintId=nextId("graph-query-builder-hint"),this.errorId=nextId("graph-query-builder-error"),this._fieldsetDisabled=!1,this._name="",this._value=EMPTY_VALUE,this.defaultValue=normalizeGraphQuery(EMPTY_VALUE),this.defaultValueCaptured=!1,this._disabled=!1,this.hasInteracted=!1,this.removalFocusGeneration=0,this.onChromeSlotChange=event=>{const slot=event.currentTarget,hasContent=slot.assignedNodes({flatten:!0}).some(node=>node.nodeType===Node.TEXT_NODE?!!node.textContent?.trim():!0);slot.name==="hint"?this.hasHintSlot=hasContent:slot.name==="error"&&(this.hasErrorSlot=hasContent)},this.internals=attachInternalsSafely(this),this.validityController=new AnchoredValidityController(this,this.internals,()=>this[VALIDITY_ANCHOR]()),installCustomErrorProperty(this,()=>this.validityController.customValidityMessage),installInvalidEventAlias(this,init=>this.emit("lr-invalid",null,init)),this.syncFormState()}disconnectedCallback(){this.removalFocusGeneration++,this.pendingRemovalFocus=void 0,super.disconnectedCallback()}get form(){return getFormOwner(this.internals)}set form(owner){setFormOwner(this,owner)}getForm(){return getFormOwner(this.internals)}get labels(){return this.internals.labels}get validity(){return this.internals.validity}get validationMessage(){return this.internals.validationMessage}get willValidate(){return this.internals.willValidate}get value(){return this._value}set value(next){const old=this._value;this._value=normalizeGraphQuery(next),this.syncFormState(),this.requestUpdate("value",old)}get name(){return this._name}set name(next){const old=this._name;this._name=next??"",this._name?this.setAttribute("name",this._name):this.removeAttribute("name"),this.requestUpdate("name",old)}get disabled(){return this._disabled}set disabled(next){const old=this._disabled;this._disabled=!!next,this.toggleAttribute("disabled",this._disabled),this.validityController&&this.syncFormState(),this.requestUpdate("disabled",old)}get effectiveDisabled(){return this.disabled||this._fieldsetDisabled}get errors(){return this.publicValidityErrors()}publicValidityErrors(){const errors={...this._errors};return this.willValidate&&this.validity.customError&&(errors.base=this.validationMessage),Object.freeze(errors)}publishValiditySnapshot(){if(!this.isConnected)return;const valid=!this.willValidate||this.validity.valid,errors=valid?Object.freeze({}):this.publicValidityErrors(),key=JSON.stringify({valid,errors});key!==this.lastValidityKey&&(this.lastValidityKey=key,this.emit("lr-validity-change",Object.freeze({valid,errors})))}[VALIDITY_ANCHOR](){const firstInvalidPart=Object.keys(this._errors)[0];if(!(!firstInvalidPart||!this.renderRoot))return this.renderRoot.querySelector(`[part="${firstInvalidPart}"]`)??void 0}computeValidation(){const errors={},flags={};return this._value.startId.trim()||(errors["start-input"]=this.localize("fieldRequired"),flags.valueMissing=!0),this._value.minHops>this._value.maxHops&&(errors["max-hops"]=this.localize("graphQueryHopRangeInvalid"),flags.rangeUnderflow=!0),{errors,flags}}syncFormState(){const{errors,flags}=this.computeValidation();this._errors=errors;let formValue=null;try{formValue=JSON.stringify(this._value)}catch{formValue=null}if(this.internals.setFormValue(formValue,formValue),this.barredFromValidation||Object.keys(flags).length===0)this.validityController.setValidity({});else{const message=Object.values(errors)[0]??"";this.validityController.setValidity(flags,message)}this.syncValidityCustomStates(),this.publishValiditySnapshot()}get barredFromValidation(){return isBarredFromValidation(this,this.internals)}syncValidityCustomStates(){syncValidityStates(this.internals,{required:!0,hasInteracted:this.hasInteracted,barred:this.barredFromValidation})}checkValidity(){return this.syncFormState(),this.internals.checkValidity()}reportValidity(){return this.hasInteracted=!0,this.syncFormState(),Object.keys(this._errors).length>0&&(this.touchedFields=new Set([...this.touchedFields,...Object.keys(this._errors)])),this.internals.reportValidity()}setCustomValidity(message){this.validityController.setCustomValidity(message??""),this.syncValidityCustomStates(),this.publishValiditySnapshot()}formResetCallback(){this.hasInteracted=!1,this.captureDefaultValue(),this.value=this.defaultValue,this.touchedFields=new Set,this.saveName=""}captureDefaultValue(){this.defaultValueCaptured||(this.defaultValue=normalizeGraphQuery(this._value),this.defaultValueCaptured=!0)}formStateRestoreCallback(state2,_mode){let restored=EMPTY_VALUE;if(typeof state2=="string")try{const parsed=JSON.parse(state2);parsed!==null&&typeof parsed=="object"&&!Array.isArray(parsed)&&(restored=normalizeGraphQuery(parsed))}catch{}this.value=restored}formDisabledCallback(disabled){this._fieldsetDisabled=disabled,this.syncFormState(),this.requestUpdate()}get liveDisabled(){return this.effectiveDisabled||this.matches(":disabled")}focusFirstControl(options){if(!this.renderRoot||this.liveDisabled)return;(this.renderRoot.querySelector('[part="start-input"]')||this.renderRoot.querySelector('[part="end-input"]')||this.renderRoot.querySelector('[part="min-hops"]')||this.renderRoot.querySelector('[part="max-hops"]')||this.renderRoot.querySelector('[part="direction"]')||this.renderRoot.querySelector('[part="save-name-input"]'))?.focus(options)}focus(options){this.focusFirstControl(options)}blur(){const active=deepActiveElementIn(this.shadowRoot);active&&typeof active.blur=="function"&&active.blur()}click(){this.focusFirstControl()}setValue(next){this.effectiveDisabled||(this.hasInteracted=!0,this.value=next,this.emit("lr-input",Object.freeze({value:this._value})))}addRelationshipType(type){!type||this._value.relationshipTypes.includes(type)||this.setValue({...this._value,relationshipTypes:[...this._value.relationshipTypes,type]})}removeRelationshipType(type){this.captureChipRemovalFocus("relationship",type,this._value.relationshipTypes),this.setValue({...this._value,relationshipTypes:this._value.relationshipTypes.filter(t=>t!==type)})}addNodeType(type){!type||this._value.nodeTypes.includes(type)||this.setValue({...this._value,nodeTypes:[...this._value.nodeTypes,type]})}removeNodeType(type){this.captureChipRemovalFocus("node-type",type,this._value.nodeTypes),this.setValue({...this._value,nodeTypes:this._value.nodeTypes.filter(t=>t!==type)})}captureChipRemovalFocus(group,value,selected){const active=this.shadowRoot?.activeElement,expectedPart=group==="relationship"?"relationship-chips":"node-type-chips";if(active?.localName!=="lr-chip"||active.getAttribute("value")!==value||active.closest("lr-chip-group")?.getAttribute("part")!==expectedPart)return;const index=selected.indexOf(value),survivors=selected.filter(candidate=>candidate!==value),targetValue=survivors[Math.min(Math.max(index,0),survivors.length-1)];this.pendingRemovalFocus={kind:"chip",group,targetValue},this.removalFocusGeneration++}markTouched(part){this.hasInteracted=!0,this.syncValidityCustomStates(),!this.touchedFields.has(part)&&(this.touchedFields=new Set(this.touchedFields).add(part))}runQuery(){if(this.effectiveDisabled||!this.reportValidity())return;const detail=()=>Object.freeze({query:normalizeGraphQuery(this._value)});this.emit("lr-before-query-run",detail(),{cancelable:!0}).defaultPrevented||this.emit("lr-query-run",detail())}saveQuery(){if(this.effectiveDisabled)return;const name=this.saveName.trim();if(!name)return;const detail=()=>Object.freeze({name,query:normalizeGraphQuery(this._value)});this.emit("lr-before-query-save",detail(),{cancelable:!0}).defaultPrevented||(this.saveName="",this.emit("lr-query-save",detail()))}loadQuery(item){if(this.effectiveDisabled)return;const detail=()=>Object.freeze({queryId:item.id,query:normalizeGraphQuery(item.query)});this.emit("lr-before-query-load",detail(),{cancelable:!0}).defaultPrevented||(this.setValue({...EMPTY_VALUE,...item.query}),this.emit("lr-query-load",detail()))}deleteQuery(item){if(this.effectiveDisabled)return;const detail=()=>Object.freeze({queryId:item.id});this.emit("lr-before-query-delete",detail(),{cancelable:!0}).defaultPrevented||this.emit("lr-query-delete",detail())}willUpdate(changed){if(super.willUpdate(changed),this.hasUpdated||this.captureDefaultValue(),!changed.has("savedQueries"))return;const active=this.shadowRoot?.activeElement;if(active?.getAttribute("part")!=="saved-delete-button")return;const focusedId=active.closest("[data-query-id]")?.dataset.queryId;if(!focusedId||this.savedQueries.some(item=>item.id===focusedId))return;const index=(changed.get("savedQueries")??[]).findIndex(item=>item.id===focusedId),target=this.savedQueries[Math.min(Math.max(index,0),this.savedQueries.length-1)];this.pendingRemovalFocus={kind:"saved",targetId:target?.id},this.removalFocusGeneration++}updated(changed){super.updated(changed),this.publishValiditySnapshot();const pending=this.pendingRemovalFocus;if(!pending)return;this.pendingRemovalFocus=void 0;const generation=this.removalFocusGeneration;this.scheduleAfterUpdate(()=>{if(!(generation!==this.removalFocusGeneration||!this.isConnected)){if(pending.kind==="chip"){if(pending.targetValue){const part=pending.group==="relationship"?"relationship-chips":"node-type-chips",chip=[...this.shadowRoot?.querySelectorAll(`[part="${part}"] lr-chip`)??[]].find(candidate=>candidate.getAttribute("value")===pending.targetValue);if(chip){chip.focus();return}}const pickerPart=pending.group==="relationship"?"relationship-picker":"node-type-picker";this.shadowRoot?.querySelector(`[part="${pickerPart}"]`)?.focus();return}if(pending.targetId){const action=[...this.shadowRoot?.querySelectorAll("[data-query-id]")??[]].find(candidate=>candidate.dataset.queryId===pending.targetId)?.querySelector('[part="saved-delete-button"]');if(action){action.focus();return}}this.shadowRoot?.querySelector('[part="save-name-input"]')?.focus()}},"graph-query-removal-focus")}hopOptions(){const limit=finiteInteger(this.hopLimit,6,1,20),options=new Set(Array.from({length:limit},(_,i)=>i+1));return options.add(this._value.minHops),options.add(this._value.maxHops),[...options].sort((a,b)=>a-b)}labelForType(options,value){return options.find(o=>o.value===value)?.label??value}renderTypeFilter(kind,options,selected,add,remove,disabled){const pickerPart=kind==="relationship"?"relationship-picker":"node-type-picker",chipsPart=kind==="relationship"?"relationship-chips":"node-type-chips",pickerLabel=kind==="relationship"?this.localize("graphQueryRelationshipTypeLabel"):this.localize("graphQueryNodeTypeLabel"),available=options.filter(o=>!selected.includes(o.value));return html`
1
+ var __decorate=function(decorators,target,key,desc){var c=arguments.length,r=c<3?target:desc===null?desc=Object.getOwnPropertyDescriptor(target,key):desc,d;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")r=Reflect.decorate(decorators,target,key,desc);else for(var i=decorators.length-1;i>=0;i--)(d=decorators[i])&&(r=(c<3?d(r):c>3?d(target,key,r):d(target,key))||r);return c>3&&r&&Object.defineProperty(target,key,r),r};import{html,nothing}from"lit";import{property,state}from"lit/decorators.js";import{LyraElement}from"../../../internal/lyra-element.js";import{installFormControlLabelSupport}from"../../../internal/form-control-labels.js";installFormControlLabelSupport();import{closeIcon}from"../../../internal/icons.js";import{AnchoredValidityController,VALIDITY_ANCHOR}from"../../../internal/anchored-validity.js";import{syncValidityStates}from"../../../internal/custom-states.js";import{finiteInteger}from"../../../internal/numbers.js";import{getNumberFormat}from"../../../internal/intl-cache.js";import{nextId}from"../../../internal/a11y.js";import{deepActiveElementIn}from"../../../internal/active-element.js";import{styles}from"./graph-query-builder.styles.js";import{attachInternalsSafely}from"../../../internal/element-internals.js";import{getFormOwner,installCustomErrorProperty,isBarredFromValidation,setFormOwner}from"../../../internal/form-associated.js";import{installInvalidEventAlias}from"../../../internal/invalid-event-alias.js";import{LYRA_DEFAULT_fieldRequired,LYRA_DEFAULT_graphQueryBuilderLabel,LYRA_DEFAULT_graphQueryDeleteWithContext,LYRA_DEFAULT_graphQueryDirectionLabel,LYRA_DEFAULT_graphQueryEndLabel,LYRA_DEFAULT_graphQueryHopRangeInvalid,LYRA_DEFAULT_graphQueryLoadWithContext,LYRA_DEFAULT_graphQueryMaxHopsLabel,LYRA_DEFAULT_graphQueryMinHopsLabel,LYRA_DEFAULT_graphQueryNodeTypeLabel,LYRA_DEFAULT_graphQueryRelationshipTypeLabel,LYRA_DEFAULT_graphQueryRun,LYRA_DEFAULT_graphQuerySaveButton,LYRA_DEFAULT_graphQuerySaveNameLabel,LYRA_DEFAULT_graphQuerySavedQueriesLabel,LYRA_DEFAULT_graphQueryStartLabel,LYRA_DEFAULT_neighborDirectionBoth,LYRA_DEFAULT_neighborDirectionIn,LYRA_DEFAULT_neighborDirectionOut,LYRA_DEFAULT_noData,LYRA_DEFAULT_select}from"../../../internal/default-strings.generated.js";const MAX_TYPES=500,MAX_SAVED_QUERIES=200,MAX_TEXT=256,EMPTY_STRINGS=Object.freeze([]),EMPTY_VALUE=Object.freeze({startId:"",endId:"",relationshipTypes:EMPTY_STRINGS,nodeTypes:EMPTY_STRINGS,direction:"both",minHops:1,maxHops:1}),EMPTY_OPTIONS=Object.freeze([]),EMPTY_SAVED=Object.freeze([]);function ownValue(record,key){try{const descriptor=Object.getOwnPropertyDescriptor(record,key);return descriptor&&"value"in descriptor?descriptor.value:void 0}catch{return}}function boundedString(value){return typeof value=="string"?value.slice(0,MAX_TEXT):""}function stringArray(value){return Array.isArray(value)?Object.freeze([...new Set(value.slice(0,MAX_TYPES).filter(entry=>typeof entry=="string").map(entry=>boundedString(entry)).filter(Boolean))]):EMPTY_STRINGS}function normalizeGraphQuery(value){const record=value!==null&&typeof value=="object"&&!Array.isArray(value)?value:EMPTY_VALUE,direction=ownValue(record,"direction");return Object.freeze({startId:boundedString(ownValue(record,"startId")),endId:boundedString(ownValue(record,"endId")),relationshipTypes:stringArray(ownValue(record,"relationshipTypes")),nodeTypes:stringArray(ownValue(record,"nodeTypes")),direction:direction==="out"||direction==="in"||direction==="both"?direction:"both",minHops:finiteInteger(typeof ownValue(record,"minHops")=="number"?ownValue(record,"minHops"):EMPTY_VALUE.minHops,EMPTY_VALUE.minHops,1,20),maxHops:finiteInteger(typeof ownValue(record,"maxHops")=="number"?ownValue(record,"maxHops"):EMPTY_VALUE.maxHops,EMPTY_VALUE.maxHops,1,20)})}function normalizeTypeOptions(value){if(!Array.isArray(value))return EMPTY_OPTIONS;const values=new Set,result=[];for(const candidate of value.slice(0,MAX_TYPES)){if(candidate===null||typeof candidate!="object"||Array.isArray(candidate))continue;const optionValue=boundedString(ownValue(candidate,"value"));if(optionValue.trim().length===0||values.has(optionValue))continue;values.add(optionValue);const label=boundedString(ownValue(candidate,"label"));result.push(Object.freeze({value:optionValue,...label?{label}:{}}))}return Object.freeze(result)}function normalizeSavedQueries(value){if(!Array.isArray(value))return EMPTY_SAVED;const ids=new Set,result=[];for(const candidate of value.slice(0,MAX_SAVED_QUERIES)){if(candidate===null||typeof candidate!="object"||Array.isArray(candidate))continue;const id=boundedString(ownValue(candidate,"id"));id.trim().length===0||ids.has(id)||(ids.add(id),result.push(Object.freeze({id,name:boundedString(ownValue(candidate,"name")),query:normalizeGraphQuery(ownValue(candidate,"query"))})))}return Object.freeze(result)}class LyraGraphQueryBuilder extends LyraElement{static{this.defaultStrings={...super.defaultStrings,fieldRequired:LYRA_DEFAULT_fieldRequired,graphQueryBuilderLabel:LYRA_DEFAULT_graphQueryBuilderLabel,graphQueryDeleteWithContext:LYRA_DEFAULT_graphQueryDeleteWithContext,graphQueryDirectionLabel:LYRA_DEFAULT_graphQueryDirectionLabel,graphQueryEndLabel:LYRA_DEFAULT_graphQueryEndLabel,graphQueryHopRangeInvalid:LYRA_DEFAULT_graphQueryHopRangeInvalid,graphQueryLoadWithContext:LYRA_DEFAULT_graphQueryLoadWithContext,graphQueryMaxHopsLabel:LYRA_DEFAULT_graphQueryMaxHopsLabel,graphQueryMinHopsLabel:LYRA_DEFAULT_graphQueryMinHopsLabel,graphQueryNodeTypeLabel:LYRA_DEFAULT_graphQueryNodeTypeLabel,graphQueryRelationshipTypeLabel:LYRA_DEFAULT_graphQueryRelationshipTypeLabel,graphQueryRun:LYRA_DEFAULT_graphQueryRun,graphQuerySaveButton:LYRA_DEFAULT_graphQuerySaveButton,graphQuerySaveNameLabel:LYRA_DEFAULT_graphQuerySaveNameLabel,graphQuerySavedQueriesLabel:LYRA_DEFAULT_graphQuerySavedQueriesLabel,graphQueryStartLabel:LYRA_DEFAULT_graphQueryStartLabel,neighborDirectionBoth:LYRA_DEFAULT_neighborDirectionBoth,neighborDirectionIn:LYRA_DEFAULT_neighborDirectionIn,neighborDirectionOut:LYRA_DEFAULT_neighborDirectionOut,noData:LYRA_DEFAULT_noData,select:LYRA_DEFAULT_select}}static{this.formAssociated=!0}static{this.styles=[LyraElement.styles,styles]}static{this.immutableEventDetails=Object.freeze(["lr-input","lr-before-query-run","lr-query-run","lr-before-query-save","lr-query-save","lr-before-query-load","lr-query-load"])}static{this.properties={customError:{attribute:"custom-error",reflect:!0,noAccessor:!0},name:{reflect:!0,noAccessor:!0},value:{attribute:!1,noAccessor:!0},disabled:{type:Boolean,reflect:!0,noAccessor:!0}}}get relationshipTypeOptions(){return this._relationshipTypeOptions}set relationshipTypeOptions(value){const previous=this._relationshipTypeOptions;this._relationshipTypeOptions=normalizeTypeOptions(value),this.requestUpdate("relationshipTypeOptions",previous)}get nodeTypeOptions(){return this._nodeTypeOptions}set nodeTypeOptions(value){const previous=this._nodeTypeOptions;this._nodeTypeOptions=normalizeTypeOptions(value),this.requestUpdate("nodeTypeOptions",previous)}get savedQueries(){return this._savedQueries}set savedQueries(value){const previous=this._savedQueries;this._savedQueries=normalizeSavedQueries(value),this.requestUpdate("savedQueries",previous)}constructor(){super(),this._relationshipTypeOptions=EMPTY_OPTIONS,this._nodeTypeOptions=EMPTY_OPTIONS,this._savedQueries=EMPTY_SAVED,this.hopLimit=6,this.label="",this.hint="",this.errorText="",this._errors={},this.touchedFields=new Set,this.saveName="",this.hasHintSlot=!1,this.hasErrorSlot=!1,this.labelId=nextId("graph-query-builder-label"),this.hintId=nextId("graph-query-builder-hint"),this.errorId=nextId("graph-query-builder-error"),this._fieldsetDisabled=!1,this._name="",this._value=EMPTY_VALUE,this.defaultValue=normalizeGraphQuery(EMPTY_VALUE),this.defaultValueCaptured=!1,this._disabled=!1,this.hasInteracted=!1,this.removalFocusGeneration=0,this.onChromeSlotChange=event=>{const slot=event.currentTarget,hasContent=slot.assignedNodes({flatten:!0}).some(node=>node.nodeType===Node.TEXT_NODE?!!node.textContent?.trim():!0);slot.name==="hint"?this.hasHintSlot=hasContent:slot.name==="error"&&(this.hasErrorSlot=hasContent)},this.internals=attachInternalsSafely(this),this.validityController=new AnchoredValidityController(this,this.internals,()=>this[VALIDITY_ANCHOR]()),installCustomErrorProperty(this,()=>this.validityController.customValidityMessage),installInvalidEventAlias(this,init=>this.emit("lr-invalid",null,init)),this.syncFormState()}disconnectedCallback(){this.removalFocusGeneration++,this.pendingRemovalFocus=void 0,super.disconnectedCallback()}get form(){return getFormOwner(this.internals)}set form(owner){setFormOwner(this,owner)}getForm(){return getFormOwner(this.internals)}get labels(){return this.internals.labels}get validity(){return this.internals.validity}get validationMessage(){return this.internals.validationMessage}get willValidate(){return this.internals.willValidate}get value(){return this._value}set value(next){const old=this._value;this._value=normalizeGraphQuery(next),this.syncFormState(),this.requestUpdate("value",old)}get name(){return this._name}set name(next){const old=this._name;this._name=next??"",this._name?this.setAttribute("name",this._name):this.removeAttribute("name"),this.requestUpdate("name",old)}get disabled(){return this._disabled}set disabled(next){const old=this._disabled;this._disabled=!!next,this.toggleAttribute("disabled",this._disabled),this.validityController&&this.syncFormState(),this.requestUpdate("disabled",old)}get effectiveDisabled(){return this.disabled||this._fieldsetDisabled}get errors(){return this.publicValidityErrors()}publicValidityErrors(){const errors={...this._errors};return this.willValidate&&this.validity.customError&&(errors.base=this.validationMessage),Object.freeze(errors)}publishValiditySnapshot(){if(!this.isConnected)return;const valid=!this.willValidate||this.validity.valid,errors=valid?Object.freeze({}):this.publicValidityErrors(),key=JSON.stringify({valid,errors});key!==this.lastValidityKey&&(this.lastValidityKey=key,this.emit("lr-validity-change",Object.freeze({valid,errors})))}[VALIDITY_ANCHOR](){const firstInvalidPart=Object.keys(this._errors)[0];if(!(!firstInvalidPart||!this.renderRoot))return this.renderRoot.querySelector(`[part="${firstInvalidPart}"]`)??void 0}computeValidation(){const errors={},flags={};return this._value.startId.trim()||(errors["start-input"]=this.localize("fieldRequired"),flags.valueMissing=!0),this._value.minHops>this._value.maxHops&&(errors["max-hops"]=this.localize("graphQueryHopRangeInvalid"),flags.rangeUnderflow=!0),{errors,flags}}syncFormState(){const{errors,flags}=this.computeValidation();this._errors=errors;let formValue=null;try{formValue=JSON.stringify(this._value)}catch{formValue=null}if(this.internals.setFormValue(formValue,formValue),this.barredFromValidation||Object.keys(flags).length===0)this.validityController.setValidity({});else{const message=Object.values(errors)[0]??"";this.validityController.setValidity(flags,message)}this.syncValidityCustomStates(),this.publishValiditySnapshot()}get barredFromValidation(){return isBarredFromValidation(this,this.internals)}syncValidityCustomStates(){syncValidityStates(this.internals,{required:!0,hasInteracted:this.hasInteracted,barred:this.barredFromValidation})}checkValidity(){return this.syncFormState(),this.internals.checkValidity()}reportValidity(){return this.hasInteracted=!0,this.syncFormState(),Object.keys(this._errors).length>0&&(this.touchedFields=new Set([...this.touchedFields,...Object.keys(this._errors)])),this.internals.reportValidity()}setCustomValidity(message){this.validityController.setCustomValidity(message??""),this.syncValidityCustomStates(),this.publishValiditySnapshot()}formResetCallback(){this.hasInteracted=!1,this.captureDefaultValue(),this.value=this.defaultValue,this.touchedFields=new Set,this.saveName=""}captureDefaultValue(){this.defaultValueCaptured||(this.defaultValue=normalizeGraphQuery(this._value),this.defaultValueCaptured=!0)}formStateRestoreCallback(state2,_mode){let restored=EMPTY_VALUE;if(typeof state2=="string")try{const parsed=JSON.parse(state2);parsed!==null&&typeof parsed=="object"&&!Array.isArray(parsed)&&(restored=normalizeGraphQuery(parsed))}catch{}this.value=restored}formDisabledCallback(disabled){this._fieldsetDisabled=disabled,this.syncFormState(),this.requestUpdate()}get liveDisabled(){return this.effectiveDisabled||this.matches(":disabled")}focusFirstControl(options){if(!this.renderRoot||this.liveDisabled)return;(this.renderRoot.querySelector('[part="start-input"]')||this.renderRoot.querySelector('[part="end-input"]')||this.renderRoot.querySelector('[part="min-hops"]')||this.renderRoot.querySelector('[part="max-hops"]')||this.renderRoot.querySelector('[part="direction"]')||this.renderRoot.querySelector('[part="save-name-input"]'))?.focus(options)}focus(options){this.focusFirstControl(options)}blur(){const active=deepActiveElementIn(this.shadowRoot);active&&typeof active.blur=="function"&&active.blur()}click(){this.focusFirstControl()}setValue(next){this.effectiveDisabled||(this.hasInteracted=!0,this.value=next,this.emit("lr-input",Object.freeze({value:this._value})))}addRelationshipType(type){!type||this._value.relationshipTypes.includes(type)||this.setValue({...this._value,relationshipTypes:[...this._value.relationshipTypes,type]})}removeRelationshipType(type){this.captureChipRemovalFocus("relationship",type,this._value.relationshipTypes),this.setValue({...this._value,relationshipTypes:this._value.relationshipTypes.filter(t=>t!==type)})}addNodeType(type){!type||this._value.nodeTypes.includes(type)||this.setValue({...this._value,nodeTypes:[...this._value.nodeTypes,type]})}removeNodeType(type){this.captureChipRemovalFocus("node-type",type,this._value.nodeTypes),this.setValue({...this._value,nodeTypes:this._value.nodeTypes.filter(t=>t!==type)})}captureChipRemovalFocus(group,value,selected){const active=this.shadowRoot?.activeElement,expectedPart=group==="relationship"?"relationship-chips":"node-type-chips";if(active?.localName!=="lr-chip"||active.getAttribute("value")!==value||active.closest("lr-chip-group")?.getAttribute("part")!==expectedPart)return;const index=selected.indexOf(value),survivors=selected.filter(candidate=>candidate!==value),targetValue=survivors[Math.min(Math.max(index,0),survivors.length-1)];this.pendingRemovalFocus={kind:"chip",group,targetValue},this.removalFocusGeneration++}markTouched(part){this.hasInteracted=!0,this.syncValidityCustomStates(),!this.touchedFields.has(part)&&(this.touchedFields=new Set(this.touchedFields).add(part))}runQuery(){if(this.effectiveDisabled||!this.reportValidity())return;const detail=()=>Object.freeze({query:normalizeGraphQuery(this._value)});this.emit("lr-before-query-run",detail(),{cancelable:!0}).defaultPrevented||this.emit("lr-query-run",detail())}saveQuery(){if(this.effectiveDisabled)return;const name=this.saveName.trim();if(!name)return;const detail=()=>Object.freeze({name,query:normalizeGraphQuery(this._value)});this.emit("lr-before-query-save",detail(),{cancelable:!0}).defaultPrevented||(this.saveName="",this.emit("lr-query-save",detail()))}loadQuery(item){if(this.effectiveDisabled)return;const detail=()=>Object.freeze({queryId:item.id,query:normalizeGraphQuery(item.query)});this.emit("lr-before-query-load",detail(),{cancelable:!0}).defaultPrevented||(this.setValue({...EMPTY_VALUE,...item.query}),this.emit("lr-query-load",detail()))}deleteQuery(item){if(this.effectiveDisabled)return;const detail=()=>Object.freeze({queryId:item.id});this.emit("lr-before-query-delete",detail(),{cancelable:!0}).defaultPrevented||this.emit("lr-query-delete",detail())}willUpdate(changed){if(super.willUpdate(changed),this.hasUpdated||this.captureDefaultValue(),!changed.has("savedQueries"))return;const active=this.shadowRoot?.activeElement;if(active?.getAttribute("part")!=="saved-delete-button")return;const focusedId=active.closest("[data-query-id]")?.dataset.queryId;if(!focusedId||this.savedQueries.some(item=>item.id===focusedId))return;const index=(changed.get("savedQueries")??[]).findIndex(item=>item.id===focusedId),target=this.savedQueries[Math.min(Math.max(index,0),this.savedQueries.length-1)];this.pendingRemovalFocus={kind:"saved",targetId:target?.id},this.removalFocusGeneration++}updated(changed){super.updated(changed),this.publishValiditySnapshot();const pending=this.pendingRemovalFocus;if(!pending)return;this.pendingRemovalFocus=void 0;const generation=this.removalFocusGeneration;this.scheduleAfterUpdate(()=>{if(!(generation!==this.removalFocusGeneration||!this.isConnected)){if(pending.kind==="chip"){if(pending.targetValue){const part=pending.group==="relationship"?"relationship-chips":"node-type-chips",chip=[...this.shadowRoot?.querySelectorAll(`[part="${part}"] lr-chip`)??[]].find(candidate=>candidate.getAttribute("value")===pending.targetValue);if(chip){chip.focus();return}}const pickerPart=pending.group==="relationship"?"relationship-picker":"node-type-picker";this.shadowRoot?.querySelector(`[part="${pickerPart}"]`)?.focus();return}if(pending.targetId){const action=[...this.shadowRoot?.querySelectorAll("[data-query-id]")??[]].find(candidate=>candidate.dataset.queryId===pending.targetId)?.querySelector('[part="saved-delete-button"]');if(action){action.focus();return}}this.shadowRoot?.querySelector('[part="save-name-input"]')?.focus()}},"graph-query-removal-focus")}hopOptions(){const limit=finiteInteger(this.hopLimit,6,1,20),options=new Set(Array.from({length:limit},(_,i)=>i+1));return options.add(this._value.minHops),options.add(this._value.maxHops),[...options].sort((a,b)=>a-b)}labelForType(options,value){return options.find(o=>o.value===value)?.label??value}renderTypeFilter(kind,options,selected,add,remove,disabled){const pickerPart=kind==="relationship"?"relationship-picker":"node-type-picker",chipsPart=kind==="relationship"?"relationship-chips":"node-type-chips",pickerLabel=kind==="relationship"?this.localize("graphQueryRelationshipTypeLabel"):this.localize("graphQueryNodeTypeLabel"),available=options.filter(o=>!selected.includes(o.value));return html`
2
2
  <div part="filter-group" data-kind=${kind}>
3
3
  <lr-select
4
4
  part=${pickerPart}
@@ -1,6 +1,18 @@
1
1
  import { type PropertyValues, type TemplateResult } from 'lit';
2
2
  import { LyraElement } from '../../../internal/lyra-element.js';
3
3
  import { type CalendarDay } from './calendar-grid.js';
4
+ /**
5
+ * Which matrix label band, if any, is frozen while the grid scrolls.
6
+ *
7
+ * One closed set rather than a boolean or a pair of booleans, because freezing one axis is a real
8
+ * request on its own: a wide matrix needs the row gutter to survive horizontal scrolling, a tall one
9
+ * needs the column band to survive vertical scrolling, and the two are independent. A boolean cannot
10
+ * express that at all, and a `sticky-row-labels`/`sticky-col-labels` pair spends two attributes and
11
+ * four states on it while leaving no single reflected value to select on in CSS. The `rows`/`cols`
12
+ * spelling matches the vocabulary the rest of this component already uses (`rowLabels`,
13
+ * `row-label-width`, `colLabels`, `col-label-height`) rather than introducing axis letters.
14
+ */
15
+ export type LyraHeatmapStickyLabels = 'none' | 'rows' | 'cols' | 'both';
4
16
  /**
5
17
  * A linear RGB ramp cannot contain more than 256 visually distinct integer
6
18
  * steps on any channel. Keeping the bucket count within that bound avoids an
@@ -227,6 +239,22 @@ export interface LyraHeatmapEventMap {
227
239
  * focus, its semantic matrix coordinate or calendar date remains the sole roving stop; removal
228
240
  * clamps to the nearest survivor, or to the stable heatmap base when no interactive cells remain.
229
241
  *
242
+ * `stickyLabels` freezes a matrix label band against the grid's own scrolling — `'rows'` pins the
243
+ * row-label gutter through horizontal scrolling, `'cols'` pins the column-label band through
244
+ * vertical scrolling, `'both'` pins both. The frozen band is repainted into its own layer from the
245
+ * same `matrixGeometry` the cells were painted with in the same pass, so it tracks a
246
+ * `row-label-width`/`col-label-height` `"auto"` re-resolution instead of hardcoding it. The default
247
+ * `'none'` renders exactly what it always did: one canvas, no scrollport.
248
+ *
249
+ * Everything positioned in canvas coordinates moves into that scrollport with the cells. The hover
250
+ * tooltip renders inside it, so it stays on the cell it describes through a scroll instead of
251
+ * drifting by the scroll offset; since `overflow: auto` there clips whatever leaves the
252
+ * scrollport, it is also kept inside the visible window — clamped along the inline axis, and
253
+ * flipped to below its cell when a frozen band leaves no room above. Arrow-key navigation scrolls
254
+ * the focused cell into that window, clear of the frozen bands: the canvas is the roving tab stop,
255
+ * its focus ring is painted into the bitmap, and it calls `preventDefault()` on the arrows, so
256
+ * without that scroll a keyboard user has no way at all to bring the focused cell back into view.
257
+ *
230
258
  * Calendar `data.columnX` overrides the x-origin computed for each
231
259
  * week column — drawing, hit-testing, the focus ring, and month-label
232
260
  * positioning all consult it consistently, so a consumer can pixel-align a
@@ -271,9 +299,14 @@ export interface LyraHeatmapEventMap {
271
299
  * resize. `detail` is the same object `matrixGeometry` returns. Never fired in calendar mode.
272
300
  * @csspart base - The heatmap wrapper.
273
301
  * @csspart canvas - The heatmap canvas.
302
+ * @csspart grid - The scrollport wrapping the canvas while `stickyLabels` freezes an axis; absent otherwise.
303
+ * @csspart row-labels - The frozen row-label gutter, rendered while `stickyLabels` is `rows` or `both`.
304
+ * @csspart col-labels - The frozen column-label band, rendered while `stickyLabels` is `cols` or `both`.
274
305
  * @csspart cells - The opt-in per-cell accessibility overlay.
275
306
  * @csspart cell - An opt-in native button for one matrix or calendar cell.
276
- * @csspart tooltip - The hover tooltip.
307
+ * @csspart tooltip - The hover tooltip, positioned over the hovered cell. It renders inside
308
+ * `[part="grid"]` while `stickyLabels` freezes an axis (so it scrolls with the cells) and as a
309
+ * `[part="base"]` child otherwise.
277
310
  * @csspart live-region - An aria-hidden shadow mirror of the keyboard announcement; the actual
278
311
  * announcement uses the shared light-DOM polite sink.
279
312
  * @csspart projection-limit - Localized assistive disclosure for bounded projections.
@@ -294,6 +327,8 @@ export interface LyraHeatmapEventMap {
294
327
  * @cssprop [--lr-heatmap-focus-ring-color=var(--lr-focus-ring-color)] - Focus ring around a focused cell.
295
328
  * @cssprop [--lr-heatmap-annotation-color=var(--lr-color-danger)] - Border color for an annotated cell.
296
329
  * @cssprop [--lr-heatmap-selected-color=var(--lr-color-success)] - Border color for the selected cell.
330
+ * @cssprop [--lr-heatmap-sticky-label-bg=var(--lr-color-surface)] - Backdrop painted under a frozen `stickyLabels` band. Must be opaque: it covers the same labels the scrolling canvas painted underneath it.
331
+ * @cssprop [--lr-heatmap-grid-max-block-size=none] - Block-size ceiling of the `stickyLabels` scrollport. A frozen column band only stays behind once the grid actually scrolls vertically.
297
332
  * @cssprop [--lr-heatmap-color-steps-gradient=linear-gradient(to right, var(--lr-heatmap-scale-lo), var(--lr-heatmap-scale-hi))] - Gradient painted on the continuous legend bar. Set on the host by the component itself while `colorSteps` is supplied, and removed again when it is not; the fallback is the two-endpoint scale ramp.
298
333
  * @status stable
299
334
  * @since 4.0.0
@@ -357,6 +392,46 @@ export declare class LyraHeatmap extends LyraElement<LyraHeatmapEventMap> {
357
392
  * would be incoherent.
358
393
  */
359
394
  colLabelRotation?: number;
395
+ private _stickyLabels;
396
+ /**
397
+ * Freezes a matrix label band against the grid's own scrolling, instead of leaving it baked into
398
+ * the scrolling bitmap. `'rows'` pins the row-label gutter so it survives horizontal scrolling,
399
+ * `'cols'` pins the column-label band so it survives vertical scrolling, `'both'` pins both, and
400
+ * the default `'none'` renders exactly what this component rendered before the option existed:
401
+ * one canvas, no scrollport, no extra elements.
402
+ *
403
+ * Labels and cells share one bitmap, so a band cannot be `position: sticky` on its own; a tall
404
+ * matrix therefore scrolled its column header away and left the columns unidentifiable. Setting
405
+ * this repaints the requested band into its own layer, in the same draw pass and from the same
406
+ * resolved `matrixGeometry` the cells were painted with, so the two cannot drift under scroll, a
407
+ * resize, a DPR change, or a `rowLabelWidth`/`colLabelHeight` `"auto"` re-resolution. That last
408
+ * one is the point: a hand-rolled light-DOM mirror had to hardcode the gutter width, which made
409
+ * it mutually exclusive with `row-label-width="auto"`.
410
+ *
411
+ * Freezing needs something to scroll, so the frozen modes wrap the grid in a `[part="grid"]`
412
+ * scrollport. It is bounded inline by the host's own allocation (a matrix wider than a 320px host
413
+ * scrolls inside the component rather than overflowing it) and unbounded in block by default; set
414
+ * `--lr-heatmap-grid-max-block-size` to bound it, since a column band can only stay behind while
415
+ * the grid actually scrolls vertically.
416
+ *
417
+ * Matrix mode only, like `matrixGeometry` and `lr-matrix-geometry-change`: calendar mode's axes
418
+ * are a different geometry (a fixed weekday gutter, a month band, and the optional `columnX`/
419
+ * `rowY` overrides), so this property is read but has no effect there.
420
+ *
421
+ * Under `dir="rtl"` the grid keeps this component's documented physical LTR geometry, so the
422
+ * scrollport is direction-pinned like the canvas already is and the bands then freeze against the
423
+ * logical inline-start/block-start edges of that pinned box — which is to say the physical left
424
+ * and top, where the labels they duplicate are actually painted.
425
+ *
426
+ * See `LyraHeatmapStickyLabels` for why this is one closed set rather than a boolean or a pair.
427
+ */
428
+ get stickyLabels(): LyraHeatmapStickyLabels;
429
+ set stickyLabels(next: LyraHeatmapStickyLabels);
430
+ /** `stickyLabels` restricted to the mode it applies to. One gate for render and paint alike, so
431
+ * switching `data` to a calendar can never leave a frozen band or a scrollport behind. */
432
+ private get effectiveStickyLabels();
433
+ private get freezesRowLabels();
434
+ private get freezesColLabels();
360
435
  /** Column band resolved during the last draw, mirroring `resolvedRowLabelWidth`. */
361
436
  private resolvedColLabelHeight;
362
437
  /** Normalized rotation in degrees: finite, clamped to `[0, 90]`. */
@@ -370,17 +445,26 @@ export declare class LyraHeatmap extends LyraElement<LyraHeatmapEventMap> {
370
445
  * `{ padLeft, padTop, cellSize }`, all in CSS pixels -- so a light-DOM consumer (e.g. a sticky
371
446
  * header mirror) can line up with the canvas without hardcoding the same numbers `row-label-width`
372
447
  * or `col-label-height`'s `"auto"` resolution would otherwise keep private. `undefined` in
373
- * calendar mode, and before the first matrix draw. Reuses the exact getters/method `drawMatrix()`
374
- * itself calls, so this can never disagree with what was actually painted. See also the
375
- * `lr-matrix-geometry-change` event, fired whenever this value changes.
448
+ * calendar mode, and before the first matrix draw.
449
+ *
450
+ * This returns the frozen object `drawMatrix()` stored (and `lr-matrix-geometry-change` carried)
451
+ * on the last draw -- not a fresh computation. That distinction is the whole contract. Computing
452
+ * it on read from the same internal getters `drawMatrix()` uses looks equivalent and is not:
453
+ * those getters read CURRENT layout, so any interval where layout has moved but no draw has
454
+ * happened yet makes the getter describe a canvas that does not exist. Two such intervals are
455
+ * routine -- full redraws pause while the host is outside the viewport (documented behaviour of
456
+ * this component), and `rowLabelWidth`/`colLabelHeight` are not redraw-triggering properties at
457
+ * all -- and both land hardest on the tall, partly-scrolled matrix this getter exists to serve.
458
+ * Returning the stored object also makes the getter and the event the same value by
459
+ * construction rather than by coincidence.
376
460
  */
377
461
  get matrixGeometry(): Readonly<LyraHeatmapMatrixGeometryChangeDetail> | undefined;
378
- /** Set on the first successful `drawMatrix()` pass; gates `matrixGeometry` returning a real
379
- * value only once there has actually been a draw to describe. */
380
- private hasDrawnMatrix;
381
- /** Last geometry `lr-matrix-geometry-change` fired, so a same-valued redraw (a color-only
382
- * update, an unrelated data change) does not refire the event. */
383
- private lastEmittedMatrixGeometry?;
462
+ /** The geometry the last `drawMatrix()` pass painted with, frozen and shared with the
463
+ * `lr-matrix-geometry-change` detail. `undefined` until the first draw, which is what gates
464
+ * `matrixGeometry` returning a real value only once there is a draw to describe. Also backs the
465
+ * event's change detection, so a same-valued redraw (a color-only update, an unrelated data
466
+ * change) does not refire it. */
467
+ private lastPaintedMatrixGeometry?;
384
468
  private get matrixPadTop();
385
469
  /** Tallest rotated column label plus inset, floored at the built-in band and capped so labels can
386
470
  * never crowd out the matrix. The column mirror of `measureRowLabelWidth()`. */
@@ -537,7 +621,10 @@ export declare class LyraHeatmap extends LyraElement<LyraHeatmapEventMap> {
537
621
  * Strictly presentation: the stops are never consulted by the color ramp, the bucket math, the
538
622
  * tooltip, or the accessible name — supplying them changes nothing a cell renders. Any
539
623
  * `annotations` with a `label` still render their `[part="legend-annotation"]` entries after
540
- * the stops. Unset (the default) or an empty array reproduces today's exact gradient legend.
624
+ * the stops. Reassigning stops whose supported fields are unchanged does not schedule a redraw;
625
+ * every assignment is still clone-owned, so mutating and reassigning the caller's array is
626
+ * detected without exposing that mutation in place. Unset (the default) or an empty array
627
+ * reproduces today's exact gradient legend.
541
628
  */
542
629
  legendStops?: readonly HeatmapLegendStop[];
543
630
  /**
@@ -768,6 +855,10 @@ export declare class LyraHeatmap extends LyraElement<LyraHeatmapEventMap> {
768
855
  private labelColor;
769
856
  /** Reads the customizable canvas axis/label font off the host's computed style. */
770
857
  private labelFont;
858
+ /** Reads the customizable frozen-label-band backdrop off the host's computed style, resolved to a
859
+ * concrete color: canvas silently keeps its previous `fillStyle` for anything it cannot parse,
860
+ * and a `var()` chain is exactly that. */
861
+ private stickyLabelBg;
771
862
  /** Reads the customizable no-data cell fill off the host's computed style. */
772
863
  private noDataFill;
773
864
  /** Reads the customizable canvas-drawn keyboard-focus-ring stroke color off the host's computed style. */
@@ -898,6 +989,32 @@ export declare class LyraHeatmap extends LyraElement<LyraHeatmapEventMap> {
898
989
  private paintCalendarFocusOverlays;
899
990
  private repaintCalendarFocusCell;
900
991
  private drawMatrix;
992
+ /** Paints the row-label gutter at `x = 0 .. padLeft`. Extracted so the cell canvas and a frozen
993
+ * `[part="row-labels"]` band paint from one routine and one set of geometry arguments — the
994
+ * band's own canvas shares the cell canvas's origin, so "aligned" is what the same coordinates
995
+ * mean, rather than something a second implementation has to keep agreeing about. */
996
+ private paintMatrixRowLabels;
997
+ /** The column-label band at `y = 0 .. padTop`, rotation included. The mirror of
998
+ * `paintMatrixRowLabels()`, shared with a frozen `[part="col-labels"]` band the same way. */
999
+ private paintMatrixColLabels;
1000
+ /**
1001
+ * Repaints whichever label bands `stickyLabels` freezes, from the geometry this very draw pass
1002
+ * painted the cells with. Each band is a canvas sharing the cell canvas's origin (both sit in the
1003
+ * same single-area grid), sized to the band it owns and offset on one axis only by
1004
+ * `position: sticky`, so it slides along the axis it is not frozen on and stays put on the one it
1005
+ * is. Nothing here recomputes geometry, which is what keeps a band aligned across a resize, a DPR
1006
+ * change, and a `row-label-width`/`col-label-height` `"auto"` re-resolution alike.
1007
+ *
1008
+ * The cell canvas keeps painting its own labels underneath: leaving that pass untouched is what
1009
+ * makes the default `'none'` byte-identical, and the band's opaque backdrop covers the duplicate
1010
+ * once it scrolls out from under the frozen copy.
1011
+ */
1012
+ private paintFrozenLabelBands;
1013
+ private frozenBandCanvas;
1014
+ /** Sizes one band's backing store to the same DPR the cell canvas uses, fills the opaque backdrop,
1015
+ * then hands the caller a context already in CSS-pixel coordinates identical to the cell
1016
+ * canvas's — so a label lands on the same physical pixel in both. */
1017
+ private paintFrozenBand;
901
1018
  /**
902
1019
  * Maps a pointer position, in canvas-local CSS px (e.g.
903
1020
  * `PointerEvent.offsetX/offsetY`), to the cell underneath it — or `null`
@@ -940,7 +1057,7 @@ export declare class LyraHeatmap extends LyraElement<LyraHeatmapEventMap> {
940
1057
  * `CalendarCellPos` in this component goes through here, so no call site can forget the date. */
941
1058
  private calendarPos;
942
1059
  /**
943
- * Pixel rect (canvas-local CSS px) of a cell — shared by `tooltipStyle()`
1060
+ * Pixel rect (canvas-local CSS px) of a cell — shared by `tooltipAnchor()`
944
1061
  * so the tooltip's position always agrees with the same geometry
945
1062
  * `drawMatrix()`/`drawCalendar()`/`hitTest*()` use.
946
1063
  */
@@ -968,8 +1085,53 @@ export declare class LyraHeatmap extends LyraElement<LyraHeatmapEventMap> {
968
1085
  * wording anywhere around the cell text instead of it being appended. */
969
1086
  private announce;
970
1087
  private emitCellClick;
971
- /** Inline `style` for `[part="tooltip"]`, centered above the given cell. */
972
- private tooltipStyle;
1088
+ /** The `stickyLabels` scrollport, or `null` in the default unfrozen render, which has none. */
1089
+ private get gridScrollport();
1090
+ /** The tooltip's own rendered box in CSS px, recorded by `measureTooltip()`: `inline` is its
1091
+ * border box, `block` that box plus the token-sized gap it keeps from the cell. Only read while
1092
+ * a scrollport can clip it; zero until the first measurement, which degrades to the unclamped
1093
+ * placement rather than to a wrong one. */
1094
+ private tooltipSize;
1095
+ /**
1096
+ * Records the tooltip's rendered size, so the next paint can keep it inside the `stickyLabels`
1097
+ * scrollport. Measured rather than derived: the box is sized by caller text, and the gap it
1098
+ * keeps from the cell is a design token whose value belongs in the stylesheet.
1099
+ *
1100
+ * Repositioning can never resize it (`white-space: nowrap`, no inline-size constraint), so the
1101
+ * re-render scheduled here always settles in one step. It runs from `scheduleAfterUpdate()`
1102
+ * rather than straight out of `updated()` on purpose: that keeps the `requestUpdate()` outside
1103
+ * Lit's own update cycle, and its microtask still flushes before paint, so the corrected
1104
+ * position is the first one the user ever sees.
1105
+ */
1106
+ private measureTooltip;
1107
+ /**
1108
+ * Inline `style` for `[part="tooltip"]` — centered on the given cell — plus whether it had to be
1109
+ * flipped below that cell.
1110
+ *
1111
+ * With no `stickyLabels` scrollport this is the original unconditional "centered above the cell"
1112
+ * placement, in the canvas-local coordinates `cellRect()` returns. With one, the tooltip renders
1113
+ * *inside* that scrollport, which is what keeps it pinned to the cell it describes through a
1114
+ * scroll — and there `overflow: auto` clips whatever leaves the scrollport, while an opaque
1115
+ * frozen band hides whatever is drawn under it. So the placement is additionally kept inside the
1116
+ * band-free part of the visible window: still centered on the cell but clamped to that window,
1117
+ * and flipped to below the cell when there is no room above it and there is room below. Flipping
1118
+ * is conditional on both, deliberately: an unconditional flip at the block end would push the
1119
+ * tooltip past the content box and grow the scrollable area under the pointer.
1120
+ */
1121
+ private tooltipAnchor;
1122
+ /**
1123
+ * Keeps the keyboard-focused cell inside the `stickyLabels` scrollport, clear of the frozen
1124
+ * bands' own boxes.
1125
+ *
1126
+ * Without this the feature is an accessibility regression: the default (unset `accessibleCells`)
1127
+ * roving stop is the canvas itself, its focus ring is painted into the canvas bitmap rather than
1128
+ * carried by a focusable element the browser would scroll to, and `onMatrixKeyDown()` calls
1129
+ * `preventDefault()` on every arrow — so the browser's own scrolling never happens either, and
1130
+ * arrowing far enough leaves the focus indicator off-screen with no keyboard way to reveal it.
1131
+ * A minimal scroll (never re-centering a cell that is already fully visible) keeps the scroll
1132
+ * position stable while arrowing around inside the visible window.
1133
+ */
1134
+ private scrollCellIntoView;
973
1135
  /**
974
1136
  * Value-equality check for a `CellPos`, since `hoverCell` is assigned a
975
1137
  * fresh object literal on every `pointermove` — Lit's `@state()`
@@ -1016,6 +1178,29 @@ export declare class LyraHeatmap extends LyraElement<LyraHeatmapEventMap> {
1016
1178
  * unset (or empty) `legendStops` therefore reproduces the pre-`legendStops` markup exactly.
1017
1179
  */
1018
1180
  private renderLegendScale;
1181
+ /**
1182
+ * The cell canvas, the opt-in per-cell overlay, and — while `stickyLabels` freezes an axis — the
1183
+ * frozen label bands, wrapped in the scrollport those bands stick to.
1184
+ *
1185
+ * Both branches emit the same canvas and overlay from one template, so the unfrozen default keeps
1186
+ * rendering exactly the elements it always did (no wrapper, no scrollport, no second canvas) with
1187
+ * no second copy of the canvas's attribute/listener surface to drift. The bands are decorative
1188
+ * duplicates of pixels the cell canvas already painted, so they carry `aria-hidden` and no role:
1189
+ * the accessible representation stays the canvas's own `role="application"` name, or the
1190
+ * `[part="cells"]` grid under `accessibleCells` — which moves inside the scrollport so the cells
1191
+ * scroll with the canvas they overlay.
1192
+ */
1193
+ private renderGridSurface;
1194
+ /**
1195
+ * The hover tooltip, rendered once: inside the `stickyLabels` scrollport's stack when an axis is
1196
+ * frozen (see `renderGridSurface()`), and as the `[part="base"]` sibling it has always been
1197
+ * otherwise, where the canvas's origin already *is* the base's.
1198
+ *
1199
+ * `dir` is carried explicitly because the scrollport pins `direction: ltr` for the physically
1200
+ * non-mirrored grid geometry, and the tooltip is the one thing in there that is prose rather
1201
+ * than geometry -- it keeps the host's own direction it inherited before the scrollport existed.
1202
+ */
1203
+ private renderTooltip;
1019
1204
  render(): TemplateResult;
1020
1205
  /** `localize()` interpolates with a bare `String(value)`, so a number handed to it renders in
1021
1206
  * ASCII digits no matter the locale -- mixing two numbering systems inside one translated