@ixfx/components 0.6.2 → 0.7.1

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 (181) hide show
  1. package/bundle/index.d.ts +1918 -835
  2. package/bundle/index.d.ts.map +1 -1
  3. package/bundle/index.js +22137 -16840
  4. package/bundle/index.js.map +1 -1
  5. package/bundle/style.css +1 -1
  6. package/dist/ac-text.d.ts +1 -1
  7. package/dist/ac-text.js +3 -2
  8. package/dist/ac-text.js.map +1 -1
  9. package/dist/{ac-token-BAa6ke09.d.ts → ac-token--_vCHXWw.d.ts} +2 -2
  10. package/dist/{ac-token-BAa6ke09.d.ts.map → ac-token--_vCHXWw.d.ts.map} +1 -1
  11. package/dist/ac-token.d.ts +1 -1
  12. package/dist/ac-token.js +3 -2
  13. package/dist/ac-token.js.map +1 -1
  14. package/dist/{button-Bn0BuLpO.js → button-DsFbV4XD.js} +5 -4
  15. package/dist/button-DsFbV4XD.js.map +1 -0
  16. package/dist/button.d.ts.map +1 -1
  17. package/dist/button.js +1 -1
  18. package/dist/checkbox.js +1 -1
  19. package/dist/{colour-picker-0stHB90J.js → colour-picker-Ddsk-7E_.js} +4 -3
  20. package/dist/{colour-picker-0stHB90J.js.map → colour-picker-Ddsk-7E_.js.map} +1 -1
  21. package/dist/colour-picker.js +1 -1
  22. package/dist/crumbs.d.ts +1 -1
  23. package/dist/crumbs.js +4 -2
  24. package/dist/crumbs.js.map +1 -1
  25. package/dist/data-display.js +1 -1
  26. package/dist/{data-grid-C8qIo1-u.d.ts → data-grid-Bh4YrQGK.d.ts} +2 -2
  27. package/dist/{data-grid-C8qIo1-u.d.ts.map → data-grid-Bh4YrQGK.d.ts.map} +1 -1
  28. package/dist/data-grid.d.ts +1 -1
  29. package/dist/data-grid.js +4 -2
  30. package/dist/data-grid.js.map +1 -1
  31. package/dist/{defaults-BCiDhZbF.js → defaults-BJwDsMH4.js} +27 -20
  32. package/dist/defaults-BJwDsMH4.js.map +1 -0
  33. package/dist/editable-label-rzXkjf1H.d.ts +75 -0
  34. package/dist/editable-label-rzXkjf1H.d.ts.map +1 -0
  35. package/dist/editable-label.d.ts +2 -75
  36. package/dist/editable-label.js +2 -1
  37. package/dist/editable-label.js.map +1 -1
  38. package/dist/{fallbacks-DEE1nCTt.js → fallbacks-DNI9xt0F.js} +2 -2
  39. package/dist/{fallbacks-DEE1nCTt.js.map → fallbacks-DNI9xt0F.js.map} +1 -1
  40. package/dist/{grouped-item-lister-_pMDwIo1.d.ts → grouped-item-lister-DnWB-eFT.d.ts} +2 -2
  41. package/dist/{grouped-item-lister-_pMDwIo1.d.ts.map → grouped-item-lister-DnWB-eFT.d.ts.map} +1 -1
  42. package/dist/grouped-item-lister.d.ts +1 -1
  43. package/dist/{hex-editor-Bk6rxhfg.js → hex-editor-BiRfwTz1.js} +2 -2
  44. package/dist/{hex-editor-Bk6rxhfg.js.map → hex-editor-BiRfwTz1.js.map} +1 -1
  45. package/dist/hex.js +1 -1
  46. package/dist/{icon-DpA9x2Ve.d.ts → icon-CDGprSdE.d.ts} +1 -12
  47. package/dist/icon-CDGprSdE.d.ts.map +1 -0
  48. package/dist/{icons-Bm2ByQ3s.js → icon-DcEEGQa_.js} +38 -12
  49. package/dist/icon-DcEEGQa_.js.map +1 -0
  50. package/dist/icons.d.ts +3 -3
  51. package/dist/icons.js +9 -3
  52. package/dist/icons.js.map +1 -0
  53. package/dist/{index-CKtLhgYj.d.ts → index-9AhaHIT2.d.ts} +3 -3
  54. package/dist/{index-CKtLhgYj.d.ts.map → index-9AhaHIT2.d.ts.map} +1 -1
  55. package/dist/index-CG2vIgPv.d.ts +101 -0
  56. package/dist/index-CG2vIgPv.d.ts.map +1 -0
  57. package/dist/{index-BtGFYfaK.d.ts → index-CS9yM8L-.d.ts} +14 -4
  58. package/dist/index-CS9yM8L-.d.ts.map +1 -0
  59. package/dist/index.d.ts +861 -71
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +4257 -931
  62. package/dist/index.js.map +1 -1
  63. package/dist/interaction-D5XdKzbR.js +2 -0
  64. package/dist/labelled-radial-input.js +2 -1
  65. package/dist/labelled-radial-input.js.map +1 -1
  66. package/dist/labelled-slider-input-DzigjMwP.js +808 -0
  67. package/dist/labelled-slider-input-DzigjMwP.js.map +1 -0
  68. package/dist/labelled-slider-input.d.ts +2 -0
  69. package/dist/labelled-slider-input.js +2 -0
  70. package/dist/{types-DlVNJez_.d.ts → list-selection-types-DSuRNWpx.d.ts} +10 -2
  71. package/dist/list-selection-types-DSuRNWpx.d.ts.map +1 -0
  72. package/dist/menu-CXDFJxNm.js +825 -0
  73. package/dist/menu-CXDFJxNm.js.map +1 -0
  74. package/dist/{menu-item-Bs2MYyHq.d.ts → menu-item-CZi9vy5O.d.ts} +4 -2
  75. package/dist/menu-item-CZi9vy5O.d.ts.map +1 -0
  76. package/dist/menu-item-D2S4i8u1.js +809 -0
  77. package/dist/menu-item-D2S4i8u1.js.map +1 -0
  78. package/dist/menu.d.ts +2 -2
  79. package/dist/menu.js +2 -1
  80. package/dist/miller.d.ts +1 -1
  81. package/dist/miller.js +3 -2
  82. package/dist/miller.js.map +1 -1
  83. package/dist/narrowed-text.js +4 -2
  84. package/dist/narrowed-text.js.map +1 -1
  85. package/dist/notification-pill-DyZydXoZ.d.ts.map +1 -1
  86. package/dist/notifier.js +4 -3
  87. package/dist/notifier.js.map +1 -1
  88. package/dist/panel.d.ts +13 -13
  89. package/dist/panel.d.ts.map +1 -1
  90. package/dist/panel.js +37 -253
  91. package/dist/panel.js.map +1 -1
  92. package/dist/plots.js +1 -1
  93. package/dist/polar-pad.js +1 -1
  94. package/dist/{radial-input-Dk8Wydp5.js → radial-input-Dp5qz0ly.js} +2 -2
  95. package/dist/{radial-input-Dk8Wydp5.js.map → radial-input-Dp5qz0ly.js.map} +1 -1
  96. package/dist/radial-input.js +1 -1
  97. package/dist/range-input.js +1 -1
  98. package/dist/range.js +1 -1
  99. package/dist/registry-BEtb8_wB.js +46 -0
  100. package/dist/registry-BEtb8_wB.js.map +1 -0
  101. package/dist/select-horiz.js +3 -2
  102. package/dist/select-horiz.js.map +1 -1
  103. package/dist/{slider-input-XppH3vcy.d.ts → slider-input-BfRUW2ZB.d.ts} +16 -3
  104. package/dist/slider-input-BfRUW2ZB.d.ts.map +1 -0
  105. package/dist/{slider-input-CT4gcF-o.js → slider-input-De4eoT11.js} +382 -67
  106. package/dist/slider-input-De4eoT11.js.map +1 -0
  107. package/dist/slider-input.d.ts +1 -1
  108. package/dist/slider-input.js +1 -1
  109. package/dist/snackbar.js +1 -1
  110. package/dist/split-layout-BFSbUZQv.js +969 -0
  111. package/dist/split-layout-BFSbUZQv.js.map +1 -0
  112. package/dist/split-layout-JK3xWVX2.d.ts +179 -0
  113. package/dist/split-layout-JK3xWVX2.d.ts.map +1 -0
  114. package/dist/split-layout.d.ts +2 -60
  115. package/dist/split-layout.js +1 -617
  116. package/dist/style.css +1 -1
  117. package/dist/swipe.js +1 -1
  118. package/dist/{tab-list-D_1cYhB_.d.ts → tab-list-CGyPt07Z.d.ts} +10 -2
  119. package/dist/tab-list-CGyPt07Z.d.ts.map +1 -0
  120. package/dist/tabs.d.ts +1 -1
  121. package/dist/tabs.js +128 -27
  122. package/dist/tabs.js.map +1 -1
  123. package/dist/{tickled-controller-BtevrRLV.d.ts → tickled-controller-CRzdIEjJ.d.ts} +7 -1
  124. package/dist/tickled-controller-CRzdIEjJ.d.ts.map +1 -0
  125. package/dist/{tickled-controller-DkAU-0qL.js → tickled-controller-h9GmJ_bU.js} +8 -2
  126. package/dist/tickled-controller-h9GmJ_bU.js.map +1 -0
  127. package/dist/titlebar-Bj5pfdw_.d.ts +50 -0
  128. package/dist/titlebar-Bj5pfdw_.d.ts.map +1 -0
  129. package/dist/titlebar.d.ts +2 -0
  130. package/dist/titlebar.js +324 -0
  131. package/dist/titlebar.js.map +1 -0
  132. package/dist/tooltip-BlvBUQxx.d.ts.map +1 -1
  133. package/dist/{tree-BO6BXI2b.js → tree-Ch4W8qXQ.js} +4 -3
  134. package/dist/{tree-BO6BXI2b.js.map → tree-Ch4W8qXQ.js.map} +1 -1
  135. package/dist/tree.d.ts +1 -1
  136. package/dist/tree.js +1 -1
  137. package/dist/vertical-list-ClnqTkF3.js +814 -0
  138. package/dist/vertical-list-ClnqTkF3.js.map +1 -0
  139. package/dist/vertical-list.d.ts +8 -9
  140. package/dist/vertical-list.d.ts.map +1 -1
  141. package/dist/vertical-list.js +1 -736
  142. package/dist/{xy-axis-B6yMjckW.js → xy-axis-DWizs0bt.js} +109 -3
  143. package/dist/xy-axis-DWizs0bt.js.map +1 -0
  144. package/dist/xy-pad.js +1 -1
  145. package/docs-user/README.md +5 -0
  146. package/docs-user/detail-list.md +398 -0
  147. package/docs-user/dock.md +461 -0
  148. package/docs-user/icons.md +4 -0
  149. package/docs-user/index.json +28 -3
  150. package/docs-user/labelled-slider-input.md +198 -0
  151. package/docs-user/llms.txt +6 -1
  152. package/docs-user/panel.md +20 -1
  153. package/docs-user/slider-input.md +55 -15
  154. package/docs-user/split-layout.md +136 -170
  155. package/docs-user/tabs.md +54 -0
  156. package/docs-user/titlebar.md +102 -0
  157. package/docs-user/toolbar.md +95 -22
  158. package/docs-user/twosplit-layout.md +254 -0
  159. package/docs-user/user-catalog.md +8 -1
  160. package/docs-user/user-theming.md +109 -0
  161. package/llms.txt +6 -1
  162. package/package.json +1 -2
  163. package/dist/button-Bn0BuLpO.js.map +0 -1
  164. package/dist/defaults-BCiDhZbF.js.map +0 -1
  165. package/dist/editable-label.d.ts.map +0 -1
  166. package/dist/icon-DpA9x2Ve.d.ts.map +0 -1
  167. package/dist/icons-Bm2ByQ3s.js.map +0 -1
  168. package/dist/index-BtGFYfaK.d.ts.map +0 -1
  169. package/dist/menu-DIfU70AA.js +0 -1609
  170. package/dist/menu-DIfU70AA.js.map +0 -1
  171. package/dist/menu-item-Bs2MYyHq.d.ts.map +0 -1
  172. package/dist/slider-input-CT4gcF-o.js.map +0 -1
  173. package/dist/slider-input-XppH3vcy.d.ts.map +0 -1
  174. package/dist/split-layout.d.ts.map +0 -1
  175. package/dist/split-layout.js.map +0 -1
  176. package/dist/tab-list-D_1cYhB_.d.ts.map +0 -1
  177. package/dist/tickled-controller-BtevrRLV.d.ts.map +0 -1
  178. package/dist/tickled-controller-DkAU-0qL.js.map +0 -1
  179. package/dist/types-DlVNJez_.d.ts.map +0 -1
  180. package/dist/vertical-list.js.map +0 -1
  181. package/dist/xy-axis-B6yMjckW.js.map +0 -1
@@ -0,0 +1,809 @@
1
+ import { t as TickledController } from "./tickled-controller-h9GmJ_bU.js";
2
+ import { n as tickledStyles, t as tickledItemStyles } from "./tickled-styles-BrAHEj1q.js";
3
+ import { t as themeFallbacks } from "./fallbacks-DNI9xt0F.js";
4
+ import { t as popupSurfaceStyles } from "./popup-D80sj2QC.js";
5
+ import { n as safeCustomElement, t as __decorate } from "./decorate-VRK8rslU.js";
6
+ import "./icon-DcEEGQa_.js";
7
+ import "./interaction-D5XdKzbR.js";
8
+ import { LitElement, css, html } from "lit";
9
+ import { property, query, state } from "lit/decorators.js";
10
+ //#region src/menu/menu-container.ts
11
+ let MenuContainer = class MenuContainer extends LitElement {
12
+ constructor(..._args) {
13
+ super(..._args);
14
+ this.tickled = new TickledController(this, {
15
+ getItems: () => {
16
+ return this._getMenuItems().map((item, i) => ({
17
+ id: i,
18
+ enabled: !item.hasAttribute(`disabled`)
19
+ }));
20
+ },
21
+ onTickle: (index) => {
22
+ const items = this._getMenuItems();
23
+ if (index !== void 0) {
24
+ const item = items[index];
25
+ item.tabIndex = 0;
26
+ item.focus({ preventScroll: true });
27
+ }
28
+ },
29
+ onStateChange: () => {
30
+ this._syncTickledClasses();
31
+ }
32
+ });
33
+ this._handleMouseOver = (e) => {
34
+ const items = this._getMenuItems();
35
+ const path = e.composedPath();
36
+ const hoveredIndex = items.findIndex((item) => path.includes(item));
37
+ if (hoveredIndex !== -1) {
38
+ const item = items[hoveredIndex];
39
+ const itemPathIndex = path.indexOf(item);
40
+ if (!path.slice(0, itemPathIndex).some((node) => node instanceof Element && node.tagName === `IXFX-MENU-CONTAINER`)) this.tickled.handlePointerEnter(hoveredIndex);
41
+ e.stopPropagation();
42
+ }
43
+ };
44
+ this._handleKeydown = (e) => {
45
+ const items = this._getMenuItems();
46
+ if (items.length === 0) return;
47
+ switch (e.key) {
48
+ case `ArrowDown`:
49
+ e.preventDefault();
50
+ e.stopPropagation();
51
+ this._focusNext(items, 1);
52
+ break;
53
+ case `ArrowUp`:
54
+ e.preventDefault();
55
+ e.stopPropagation();
56
+ this._focusNext(items, -1);
57
+ break;
58
+ case `Home`:
59
+ e.preventDefault();
60
+ e.stopPropagation();
61
+ this._focusItem(items, 0);
62
+ break;
63
+ case `End`:
64
+ e.preventDefault();
65
+ e.stopPropagation();
66
+ this._focusItem(items, items.length - 1);
67
+ break;
68
+ case `Enter`:
69
+ case ` `:
70
+ e.preventDefault();
71
+ e.stopPropagation();
72
+ this._activateFocusedItem(items);
73
+ }
74
+ };
75
+ }
76
+ static {
77
+ this.styles = [
78
+ popupSurfaceStyles,
79
+ themeFallbacks,
80
+ tickledStyles,
81
+ css`
82
+ :host {
83
+ display: block;
84
+ }
85
+
86
+ .popover {
87
+ padding: var(--space-xs) 0;
88
+ min-width: 160px;
89
+ }
90
+
91
+ :host {
92
+ opacity: 1;
93
+ transform: translateY(0) scale(1);
94
+ transition: opacity 0.12s ease-out, transform 0.12s ease-out,
95
+ display 0.12s allow-discrete, overlay 0.12s allow-discrete;
96
+ }
97
+
98
+ @starting-style :host {
99
+ opacity: 0;
100
+ transform: translateY(-4px) scale(0.97);
101
+ }
102
+
103
+ ::slotted(menu-separator) {
104
+ display: block;
105
+ margin: var(--space-xs) 0;
106
+ }
107
+ `
108
+ ];
109
+ }
110
+ _updateIconSections() {
111
+ const slot = this.shadowRoot?.querySelector(`slot`);
112
+ if (!slot) return;
113
+ const assigned = slot.assignedElements({ flatten: true });
114
+ const sections = [];
115
+ let section = [];
116
+ for (const el of assigned) if (el.tagName === `IXFX-MENU-SEPARATOR` || el.tagName === `IXFX-MENU-HEADER`) {
117
+ if (section.length > 0) {
118
+ sections.push(section);
119
+ section = [];
120
+ }
121
+ } else section.push(el);
122
+ if (section.length > 0) sections.push(section);
123
+ this._propagateContext();
124
+ for (const sec of sections) {
125
+ const hasIcon = sec.some((el) => {
126
+ if (el.tagName === `IXFX-MENU-RADIO-GROUP`) return [...el.children].some((ri) => ri.hasAttribute(`icon-name`));
127
+ return el.hasAttribute(`icon-name`);
128
+ });
129
+ for (const el of sec) if (el.tagName === `IXFX-MENU-RADIO-GROUP`) for (const ri of el.children) if (hasIcon) ri.setAttribute(`data-icon-section`, ``);
130
+ else ri.removeAttribute(`data-icon-section`);
131
+ else if (hasIcon) el.setAttribute(`data-icon-section`, ``);
132
+ else el.removeAttribute(`data-icon-section`);
133
+ }
134
+ this.performUpdate();
135
+ }
136
+ _propagateContext() {
137
+ const slot = this.shadowRoot?.querySelector(`slot`);
138
+ if (!slot) return;
139
+ for (const el of slot.assignedElements({ flatten: true })) {
140
+ if (`registry` in el) el.registry = this.registry;
141
+ if (`keyboardManager` in el) el.keyboardManager = this.keyboardManager;
142
+ }
143
+ }
144
+ updated(changedProperties) {
145
+ if (changedProperties.has(`registry`) || changedProperties.has(`keyboardManager`)) this._propagateContext();
146
+ }
147
+ /**
148
+ * Sync data-input-mode to :host so CSS variables cascade to slotted items
149
+ */
150
+ willUpdate(changed) {
151
+ super.willUpdate(changed);
152
+ this.setAttribute(`data-input-mode`, this.tickled.inputMode);
153
+ }
154
+ render() {
155
+ return html`
156
+ <div
157
+ class="popover popup-surface"
158
+ role="menu"
159
+ @keydown=${this._handleKeydown}
160
+ @mouseover=${this._handleMouseOver}
161
+ >
162
+ <slot @slotchange=${this._updateIconSections}></slot>
163
+ </div>
164
+ `;
165
+ }
166
+ _getMenuItems() {
167
+ const slot = this.shadowRoot?.querySelector(`slot`);
168
+ if (!slot) return [];
169
+ const skipTags = [`IXFX-MENU-SEPARATOR`, `IXFX-MENU-HEADER`];
170
+ const result = [];
171
+ for (const el of slot.assignedElements({ flatten: true })) {
172
+ if (skipTags.includes(el.tagName)) continue;
173
+ if (el.hasAttribute(`disabled`)) continue;
174
+ const role = el.getAttribute(`role`);
175
+ if (role === `none` || role === `presentation`) continue;
176
+ if (el.tagName === `IXFX-MENU-RADIO-GROUP`) {
177
+ const groupSlot = el.shadowRoot?.querySelector(`slot`);
178
+ for (const radioItem of groupSlot?.assignedElements({ flatten: true }) ?? []) if (!radioItem.hasAttribute(`disabled`)) result.push(radioItem);
179
+ } else result.push(el);
180
+ }
181
+ return result;
182
+ }
183
+ /**
184
+ * Sync .tickled class on slotted items to match controller state
185
+ * This is called via onStateChange whenever the tickled state changes
186
+ */
187
+ _syncTickledClasses() {
188
+ const items = this._getMenuItems();
189
+ items.forEach((item, i) => {
190
+ if (this.tickled.isTickled(i)) {
191
+ item.classList.add(`tickled`);
192
+ item.tabIndex = 0;
193
+ const innerItem = item.shadowRoot?.querySelector(`.item`);
194
+ if (innerItem) {
195
+ innerItem.style.background = `rgba(0, 0, 0, 0.06)`;
196
+ innerItem.style.borderColor = `var(--accent)`;
197
+ innerItem.style.color = `inherit`;
198
+ }
199
+ } else {
200
+ item.classList.remove(`tickled`);
201
+ item.tabIndex = -1;
202
+ const innerItem = item.shadowRoot?.querySelector(`.item`);
203
+ if (innerItem) {
204
+ innerItem.style.background = ``;
205
+ innerItem.style.borderColor = ``;
206
+ innerItem.style.color = ``;
207
+ }
208
+ }
209
+ });
210
+ if (this.tickled.inputMode === `keyboard` && this.tickled.tickledId !== void 0) items[this.tickled.tickledId]?.focus();
211
+ }
212
+ focusFirst() {
213
+ this.tickled.handleKeyboardNavigation(`first`);
214
+ }
215
+ _focusNext(items, direction) {
216
+ if (items.length === 0) return;
217
+ this.tickled.handleKeyboardNavigation(direction > 0 ? `next` : `previous`);
218
+ }
219
+ _focusItem(items, index) {
220
+ if (items.length === 0) return;
221
+ const clampedIndex = Math.max(0, Math.min(index, items.length - 1));
222
+ this.tickled.handleKeyboardNavigation(clampedIndex);
223
+ }
224
+ _activateFocusedItem(items) {
225
+ const tickledIndex = this.tickled.tickledId;
226
+ if (tickledIndex === void 0) return;
227
+ const item = items[tickledIndex];
228
+ if (!item) return;
229
+ const tagName = item.tagName;
230
+ if (tagName === `IXFX-MENU-SUB-TRIGGER`) return;
231
+ if (tagName === `IXFX-MENU-CHECK-ITEM`) {
232
+ const checkItem = item;
233
+ checkItem.checked = !checkItem.checked;
234
+ checkItem.dispatchEvent(new CustomEvent(`command`, {
235
+ detail: {
236
+ command: checkItem.command,
237
+ checked: checkItem.checked
238
+ },
239
+ bubbles: true,
240
+ composed: true
241
+ }));
242
+ return;
243
+ }
244
+ if (tagName === `IXFX-MENU-RADIO-ITEM`) {
245
+ item.dispatchEvent(new CustomEvent(`radio-select`, {
246
+ detail: { value: item.value },
247
+ bubbles: true,
248
+ composed: true
249
+ }));
250
+ return;
251
+ }
252
+ if (tagName === `IXFX-MENU-ITEM`) {
253
+ const command = item.command;
254
+ if (command) item.dispatchEvent(new CustomEvent(`command`, {
255
+ detail: { command },
256
+ bubbles: true,
257
+ composed: true
258
+ }));
259
+ return;
260
+ }
261
+ item.click();
262
+ }
263
+ };
264
+ __decorate([property({
265
+ type: String,
266
+ attribute: `anchor`
267
+ })], MenuContainer.prototype, "anchor", void 0);
268
+ __decorate([property({ attribute: false })], MenuContainer.prototype, "registry", void 0);
269
+ __decorate([property({ attribute: false })], MenuContainer.prototype, "keyboardManager", void 0);
270
+ __decorate([query(`.popover`)], MenuContainer.prototype, "_popover", void 0);
271
+ MenuContainer = __decorate([safeCustomElement(`ixfx-menu-container`)], MenuContainer);
272
+ //#endregion
273
+ //#region src/menu/menu-trigger.ts
274
+ var _MenuTrigger;
275
+ let MenuTrigger = class MenuTrigger extends LitElement {
276
+ static {
277
+ _MenuTrigger = this;
278
+ }
279
+ constructor(..._args) {
280
+ super(..._args);
281
+ this.mode = `trigger`;
282
+ this.label = `⋮`;
283
+ this.iconName = ``;
284
+ this.placement = `bottom-start`;
285
+ this.tabStop = true;
286
+ this._isOpen = false;
287
+ this._hasMenu = false;
288
+ this._hasTriggerSlot = false;
289
+ this._anchorName = `--mt-${_MenuTrigger._count++}`;
290
+ this._clickListener = this._handleClickOutside.bind(this);
291
+ this._handlePopoverToggle = (e) => {
292
+ if (e.newState === `closed`) {
293
+ this._isOpen = false;
294
+ document.removeEventListener(`click`, this._clickListener);
295
+ this.dispatchEvent(new CustomEvent(`menu-trigger-close`, {
296
+ bubbles: true,
297
+ composed: true
298
+ }));
299
+ }
300
+ };
301
+ this._handleKeydown = (e) => {
302
+ if (!this._isOpen) return;
303
+ if (e.key === `Escape`) {
304
+ e.preventDefault();
305
+ e.stopPropagation();
306
+ this._closeMenu();
307
+ this._triggerBtn?.focus();
308
+ } else if (e.key === `Tab`) {
309
+ this._closeMenu();
310
+ this._triggerBtn?.focus();
311
+ }
312
+ };
313
+ }
314
+ static {
315
+ this._count = 0;
316
+ }
317
+ connectedCallback() {
318
+ super.connectedCallback();
319
+ document.addEventListener(`keydown`, this._handleKeydown);
320
+ this.addEventListener(`command`, this._handleCommand);
321
+ this.addEventListener(`radio-change`, this._handleRadioChange);
322
+ }
323
+ disconnectedCallback() {
324
+ super.disconnectedCallback();
325
+ document.removeEventListener(`keydown`, this._handleKeydown);
326
+ this.removeEventListener(`command`, this._handleCommand);
327
+ this.removeEventListener(`radio-change`, this._handleRadioChange);
328
+ document.removeEventListener(`click`, this._clickListener);
329
+ }
330
+ firstUpdated() {
331
+ this.style.setProperty(`--anchor-name`, this._anchorName);
332
+ this._popup.addEventListener(`toggle`, this._handlePopoverToggle);
333
+ this.updateComplete.then(() => {
334
+ const slot = this.shadowRoot?.querySelector(`slot[name="menu"]`);
335
+ if (slot) {
336
+ if (slot.assignedElements({ flatten: true }).length > 0) {
337
+ this._hasMenu = true;
338
+ this._propagateContext();
339
+ }
340
+ }
341
+ this._updatePosition();
342
+ });
343
+ }
344
+ updated(changedProperties) {
345
+ if (changedProperties.has(`registry`) || changedProperties.has(`keyboardManager`)) this._propagateContext();
346
+ }
347
+ _updatePosition() {
348
+ const pos = {
349
+ "bottom-start": { insetArea: `block-end span-inline-start` },
350
+ "bottom-end": { insetArea: `block-end span-inline-end` },
351
+ "right-start": { insetArea: `inline-end span-block-start` }
352
+ }[this.placement];
353
+ this._popup.style.insetArea = pos.insetArea;
354
+ this._popup.style.positionAnchor = this._anchorName;
355
+ const fb = {
356
+ "bottom-start": {
357
+ top: `anchor(bottom)`,
358
+ left: `anchor(left)`,
359
+ right: `auto`
360
+ },
361
+ "bottom-end": {
362
+ top: `anchor(bottom)`,
363
+ left: `auto`,
364
+ right: `anchor(left)`
365
+ },
366
+ "right-start": {
367
+ top: `anchor(top)`,
368
+ left: `anchor(right)`,
369
+ right: `auto`
370
+ }
371
+ }[this.placement];
372
+ this._popup.style.top = fb.top;
373
+ this._popup.style.left = fb.left;
374
+ this._popup.style.right = fb.right;
375
+ }
376
+ _propagateContext() {
377
+ const items = (this.shadowRoot?.querySelector(`slot[name="menu"]`))?.assignedElements({ flatten: true }) ?? [];
378
+ for (const el of items) {
379
+ if (`registry` in el) el.registry = this.registry;
380
+ if (`keyboardManager` in el) el.keyboardManager = this.keyboardManager;
381
+ }
382
+ }
383
+ _handleSlotChange(e) {
384
+ const assigned = e.target.assignedElements({ flatten: true });
385
+ this._hasMenu = assigned.length > 0;
386
+ this._propagateContext();
387
+ }
388
+ _handleTriggerSlotChange(e) {
389
+ const slot = e.target;
390
+ this._hasTriggerSlot = slot.assignedElements({ flatten: true }).length > 0;
391
+ }
392
+ get isOpen() {
393
+ return this._isOpen;
394
+ }
395
+ openMenu() {
396
+ this._openMenu();
397
+ }
398
+ closeMenu() {
399
+ this._closeMenu();
400
+ }
401
+ focusTrigger() {
402
+ this._triggerBtn?.focus();
403
+ }
404
+ _openMenu() {
405
+ if (this._isOpen || !this._popup?.popover) return;
406
+ try {
407
+ this._popup.showPopover({ source: this });
408
+ this._isOpen = true;
409
+ document.addEventListener(`click`, this._clickListener);
410
+ this.dispatchEvent(new CustomEvent(`menu-trigger-open`, {
411
+ bubbles: true,
412
+ composed: true
413
+ }));
414
+ requestAnimationFrame(() => {
415
+ this._getMenuContainer()?.focusFirst();
416
+ });
417
+ } catch (err) {
418
+ console.warn(`showPopover failed:`, err);
419
+ }
420
+ }
421
+ _closeMenu() {
422
+ if (!this._isOpen) return;
423
+ if (this._popup?.popover) try {
424
+ this._popup.hidePopover();
425
+ } catch {}
426
+ this._isOpen = false;
427
+ document.removeEventListener(`click`, this._clickListener);
428
+ }
429
+ _handleTriggerClick(e) {
430
+ e.stopPropagation();
431
+ if (this._isOpen) {
432
+ this._closeMenu();
433
+ this._triggerBtn?.focus();
434
+ } else this._openMenu();
435
+ }
436
+ _handleClickOutside(e) {
437
+ if (!this.contains(e.target)) {
438
+ if (this._popup?.popover && this._popup.hasAttribute(`open`)) try {
439
+ this._popup.hidePopover();
440
+ } catch {}
441
+ }
442
+ }
443
+ _getMenuContainer() {
444
+ return ((this.shadowRoot?.querySelector(`slot[name="menu"]`))?.assignedElements({ flatten: true }) ?? []).find((el) => el.tagName === `IXFX-MENU-CONTAINER`) ?? null;
445
+ }
446
+ _handleCommand(e) {
447
+ if (this._popup?.popover) try {
448
+ this._popup.hidePopover();
449
+ } catch {}
450
+ this.dispatchEvent(new CustomEvent(`menu-command`, {
451
+ detail: e.detail,
452
+ bubbles: true,
453
+ composed: true
454
+ }));
455
+ }
456
+ _handleRadioChange(e) {
457
+ if (this._popup?.popover) try {
458
+ this._popup.hidePopover();
459
+ } catch {}
460
+ this.dispatchEvent(new CustomEvent(`menu-command`, {
461
+ detail: {
462
+ command: e.detail.name,
463
+ value: e.detail.value
464
+ },
465
+ bubbles: true,
466
+ composed: true
467
+ }));
468
+ }
469
+ _handleContextMenu(e) {
470
+ if (this.mode !== `context`) return;
471
+ e.preventDefault();
472
+ this._popup.style.top = `${e.clientY}px`;
473
+ this._popup.style.left = `${e.clientX}px`;
474
+ this._popup.style.position = `fixed`;
475
+ this._popup.style.positionAnchor = `none`;
476
+ if (this._popup?.popover) try {
477
+ this._popup.showPopover();
478
+ } catch {}
479
+ }
480
+ static {
481
+ this.styles = [
482
+ themeFallbacks,
483
+ popupSurfaceStyles,
484
+ css`
485
+ :host {
486
+ display: inline-block;
487
+ position: relative;
488
+ --ixfx-button-padding: var(--ixfx-button-padding, var(--space-s) var(--space-m));
489
+ }
490
+
491
+ .trigger-area {
492
+ anchor-name: var(--anchor-name);
493
+ /* Block flex (not inline-flex): as an inline-level box this would join
494
+ the host's inline formatting context and pick up strut/baseline gaps
495
+ above and below the button, offsetting the glyph from center. */
496
+ display: flex;
497
+ align-items: stretch;
498
+ }
499
+
500
+ .trigger-btn {
501
+ display: inline-flex;
502
+ align-items: center;
503
+ justify-content: center;
504
+ line-height: 1;
505
+ background: none;
506
+ border: none;
507
+ color: var(--surface-3-text);
508
+ cursor: pointer;
509
+ padding: var(--ixfx-button-padding);
510
+ font-family: var(--font-family, inherit);
511
+ font-size: var(--text-m);
512
+ font-weight: 600;
513
+ border-radius: var(--radius-s);
514
+ opacity: 0.6;
515
+ transition: opacity var(--transition), background var(--transition);
516
+ }
517
+
518
+ .trigger-btn:hover {
519
+ background: var(--_trigger-hover-bg, var(--surface-4));
520
+ color: var(--surface-3-text);
521
+ opacity: var(--_trigger-hover-opacity, 1);
522
+ }
523
+
524
+ .trigger-btn:focus-visible {
525
+ background: var(--surface-4);
526
+ color: var(--surface-3-text);
527
+ outline: none;
528
+ opacity: 1;
529
+ }
530
+
531
+ .popup {
532
+ position: fixed;
533
+ position-anchor: var(--anchor-name);
534
+ inset: auto;
535
+ margin: 8px;
536
+ padding: 0;
537
+ border: none;
538
+ background: transparent;
539
+ z-index: 100;
540
+ max-height: min(60vh, 400px);
541
+ max-width: min(90vw, 320px);
542
+ overflow: auto;
543
+ overscroll-behavior: contain;
544
+ position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline;
545
+ }
546
+
547
+ .popup[popover] {
548
+ opacity: 1;
549
+ transform: translateY(0) scale(1);
550
+ transition: opacity 0.12s ease-out, transform 0.12s ease-out,
551
+ display 0.12s allow-discrete, overlay 0.12s allow-discrete;
552
+ }
553
+
554
+ @starting-style .popup {
555
+ opacity: 0;
556
+ transform: translateY(-4px) scale(0.97);
557
+ }
558
+ `
559
+ ];
560
+ }
561
+ render() {
562
+ const showButton = this.mode !== `dropdown` && this._hasMenu && !this._hasTriggerSlot;
563
+ return html`
564
+ <div
565
+ class="trigger-area"
566
+ @click=${this._handleTriggerClick}
567
+ @contextmenu=${this._handleContextMenu}
568
+ >
569
+ ${showButton ? html`<button class="trigger-btn" part="base" tabindex=${this.tabStop ? `0` : `-1`}>${this.iconName ? html`<ixfx-icon name=${this.iconName}></ixfx-icon>` : this.label}</button>` : null}
570
+ <slot name="trigger" @slotchange=${this._handleTriggerSlotChange}></slot>
571
+ </div>
572
+ <div
573
+ class="popup popup-surface"
574
+ popover="auto"
575
+ @click=${(e) => e.stopPropagation()}
576
+ >
577
+ <slot name="menu" @slotchange=${this._handleSlotChange}></slot>
578
+ </div>
579
+ `;
580
+ }
581
+ };
582
+ __decorate([property({ type: String })], MenuTrigger.prototype, "mode", void 0);
583
+ __decorate([property({ type: String })], MenuTrigger.prototype, "label", void 0);
584
+ __decorate([property({
585
+ type: String,
586
+ attribute: `icon-name`
587
+ })], MenuTrigger.prototype, "iconName", void 0);
588
+ __decorate([property({ attribute: false })], MenuTrigger.prototype, "registry", void 0);
589
+ __decorate([property({ attribute: false })], MenuTrigger.prototype, "keyboardManager", void 0);
590
+ __decorate([property({ type: String })], MenuTrigger.prototype, "placement", void 0);
591
+ __decorate([property({
592
+ type: Boolean,
593
+ attribute: `tab-stop`
594
+ })], MenuTrigger.prototype, "tabStop", void 0);
595
+ __decorate([state()], MenuTrigger.prototype, "_isOpen", void 0);
596
+ __decorate([state()], MenuTrigger.prototype, "_hasMenu", void 0);
597
+ __decorate([state()], MenuTrigger.prototype, "_hasTriggerSlot", void 0);
598
+ __decorate([query(`.trigger-btn`)], MenuTrigger.prototype, "_triggerBtn", void 0);
599
+ __decorate([query(`.popup`)], MenuTrigger.prototype, "_popup", void 0);
600
+ MenuTrigger = _MenuTrigger = __decorate([safeCustomElement(`ixfx-menu-trigger`)], MenuTrigger);
601
+ //#endregion
602
+ //#region src/menu/menu-item.ts
603
+ let MenuItem = class MenuItem extends LitElement {
604
+ constructor(..._args) {
605
+ super(..._args);
606
+ this.command = ``;
607
+ this.commandArgs = ``;
608
+ this.label = ``;
609
+ this.disabled = false;
610
+ this.iconName = ``;
611
+ this._hasChildren = false;
612
+ this._hoverTimeout = null;
613
+ }
614
+ disconnectedCallback() {
615
+ super.disconnectedCallback();
616
+ if (this._hoverTimeout) clearTimeout(this._hoverTimeout);
617
+ }
618
+ _handleSlotChange() {
619
+ const assigned = this._slot?.assignedElements() ?? [];
620
+ this._hasChildren = assigned.length > 0;
621
+ }
622
+ _handleMouseEnter() {
623
+ if (this.disabled || !this._hasChildren || !this.submenuPopover) return;
624
+ if (this._hoverTimeout) clearTimeout(this._hoverTimeout);
625
+ this._hoverTimeout = window.setTimeout(() => {
626
+ this._showSubmenu();
627
+ }, 150);
628
+ }
629
+ _handleMouseLeave() {
630
+ if (this._hoverTimeout) {
631
+ clearTimeout(this._hoverTimeout);
632
+ this._hoverTimeout = null;
633
+ }
634
+ this._hideSubmenu();
635
+ }
636
+ _showSubmenu() {
637
+ if (!this.submenuPopover?.popover) return;
638
+ try {
639
+ this.submenuPopover.showPopover();
640
+ } catch (err) {
641
+ console.warn(`showPopover failed:`, err);
642
+ }
643
+ }
644
+ _hideSubmenu() {
645
+ if (!this.submenuPopover?.popover) return;
646
+ try {
647
+ this.submenuPopover.hidePopover();
648
+ } catch (err) {
649
+ console.warn(`hidePopover failed:`, err);
650
+ }
651
+ }
652
+ handleClick() {
653
+ if (this.disabled) return;
654
+ this.dispatchEvent(new CustomEvent(`command`, {
655
+ detail: {
656
+ command: this.command,
657
+ args: this.commandArgs
658
+ },
659
+ bubbles: true,
660
+ composed: true
661
+ }));
662
+ }
663
+ _resolvedLabel() {
664
+ if (this.label) return this.label;
665
+ if (this.registry && this.command) return this.registry.get(this.command)?.label ?? ``;
666
+ return ``;
667
+ }
668
+ _resolvedIcon() {
669
+ if (this.iconName) return this.iconName;
670
+ if (this.registry && this.command) return this.registry.get(this.command)?.icon ?? null;
671
+ return null;
672
+ }
673
+ willUpdate(changedProperties) {
674
+ super.willUpdate(changedProperties);
675
+ const resolvedIcon = this._resolvedIcon();
676
+ if (resolvedIcon && !this.iconName) this.setAttribute(`data-icon-section`, ``);
677
+ else if (!resolvedIcon && !this.iconName) this.removeAttribute(`data-icon-section`);
678
+ }
679
+ updated(changedProperties) {
680
+ super.updated(changedProperties);
681
+ }
682
+ _getShortcut() {
683
+ if (!this.keyboardManager || !this.command) return null;
684
+ const binding = (this.keyboardManager.getBindings?.() ?? []).find((b) => b.commandId === this.command);
685
+ if (!binding) return null;
686
+ const mods = Array.from(binding.modifiers ?? [], (m) => ({
687
+ ctrl: `⌃`,
688
+ shift: `⇧`,
689
+ alt: `⌥`,
690
+ meta: `⌘`
691
+ })[m] ?? m);
692
+ const key = binding.key?.length === 1 ? binding.key.toUpperCase() : binding.key;
693
+ return [...mods, key].join(``);
694
+ }
695
+ static {
696
+ this.styles = [
697
+ themeFallbacks,
698
+ tickledItemStyles,
699
+ css`
700
+ :host {
701
+ display: block;
702
+ }
703
+
704
+ .item {
705
+ display: flex !important;
706
+ align-items: center;
707
+ padding: 6px var(--space-m);
708
+ font-size: var(--text-m);
709
+ background: var(--item-bg, transparent);
710
+ border: var(--item-border, 1px solid transparent);
711
+ color: var(--item-text, inherit);
712
+ }
713
+
714
+ :host(.tickled) .item {
715
+ background: var(--item-bg-tickled);
716
+ border: var(--item-border-tickled);
717
+ color: var(--item-text-tickled);
718
+ }
719
+
720
+ :host(:focus) {
721
+ outline: none;
722
+ }
723
+
724
+ .item.disabled {
725
+ opacity: var(--disabled-opacity);
726
+ cursor: default;
727
+ }
728
+
729
+ .item.disabled:hover {
730
+ background: transparent;
731
+ }
732
+
733
+ .icon-slot {
734
+ width: 0;
735
+ overflow: hidden;
736
+ flex-shrink: 0;
737
+ display: inline-flex;
738
+ align-items: center;
739
+ justify-content: center;
740
+ }
741
+
742
+ :host([icon-name]) .icon-slot,
743
+ :host([data-icon-section]) .icon-slot {
744
+ width: 1em;
745
+ height: 1em;
746
+ margin-right: var(--space-s);
747
+ }
748
+
749
+ .label {
750
+ flex: 1;
751
+ }
752
+
753
+ .shortcut {
754
+ margin-left: auto;
755
+ color: var(--surface-muted-text);
756
+ font-size: var(--text-s);
757
+ }
758
+
759
+ .arrow {
760
+ color: var(--surface-muted-text);
761
+ margin-left: var(--space-m);
762
+ }
763
+ `
764
+ ];
765
+ }
766
+ render() {
767
+ const shortcut = this._getShortcut();
768
+ const resolvedIcon = this._resolvedIcon();
769
+ return html`
770
+ <div
771
+ class="item ${this.disabled ? `disabled` : ``}"
772
+ @click=${this.handleClick}
773
+ @mouseenter=${this._handleMouseEnter}
774
+ @mouseleave=${this._handleMouseLeave}
775
+ tabindex="${this.disabled ? -1 : 0}"
776
+ role="menuitem"
777
+ aria-haspopup="${this._hasChildren ? `true` : `false`}"
778
+ aria-expanded="${this.submenuPopover?.matches(`:popover-open`) ?? false}"
779
+ >
780
+ <span class="icon-slot">${resolvedIcon ? html`<ixfx-icon name="${resolvedIcon}"></ixfx-icon>` : null}</span>
781
+ <span class="label"><slot></slot>${this._resolvedLabel()}</span>
782
+ ${shortcut ? html`<span class="shortcut">${shortcut}</span>` : null}
783
+ ${this._hasChildren ? html`<span class="arrow"><ixfx-icon name="chevron-right"></ixfx-icon></span>` : null}
784
+ </div>
785
+ <slot @slotchange=${this._handleSlotChange}></slot>
786
+ `;
787
+ }
788
+ };
789
+ __decorate([property({ type: String })], MenuItem.prototype, "command", void 0);
790
+ __decorate([property({
791
+ type: String,
792
+ attribute: `command-args`
793
+ })], MenuItem.prototype, "commandArgs", void 0);
794
+ __decorate([property({ type: String })], MenuItem.prototype, "label", void 0);
795
+ __decorate([property({ type: Boolean })], MenuItem.prototype, "disabled", void 0);
796
+ __decorate([property({
797
+ type: String,
798
+ attribute: `icon-name`
799
+ })], MenuItem.prototype, "iconName", void 0);
800
+ __decorate([property({ attribute: false })], MenuItem.prototype, "registry", void 0);
801
+ __decorate([property({ attribute: false })], MenuItem.prototype, "keyboardManager", void 0);
802
+ __decorate([property({ attribute: false })], MenuItem.prototype, "submenuPopover", void 0);
803
+ __decorate([state()], MenuItem.prototype, "_hasChildren", void 0);
804
+ __decorate([query(`slot`)], MenuItem.prototype, "_slot", void 0);
805
+ MenuItem = __decorate([safeCustomElement(`ixfx-menu-item`)], MenuItem);
806
+ //#endregion
807
+ export { MenuTrigger as n, MenuContainer as r, MenuItem as t };
808
+
809
+ //# sourceMappingURL=menu-item-D2S4i8u1.js.map