@ixfx/components 0.6.2 → 0.7.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 (152) hide show
  1. package/bundle/index.d.ts +1490 -615
  2. package/bundle/index.d.ts.map +1 -1
  3. package/bundle/index.js +20477 -15855
  4. package/bundle/index.js.map +1 -1
  5. package/bundle/style.css +1 -1
  6. package/dist/ac-text.js +3 -2
  7. package/dist/ac-text.js.map +1 -1
  8. package/dist/ac-token.js +3 -2
  9. package/dist/ac-token.js.map +1 -1
  10. package/dist/{button-Bn0BuLpO.js → button-Dat2yHAU.js} +4 -3
  11. package/dist/button-Dat2yHAU.js.map +1 -0
  12. package/dist/button.js +1 -1
  13. package/dist/checkbox.js +1 -1
  14. package/dist/{colour-picker-0stHB90J.js → colour-picker-BSDBL-QL.js} +4 -3
  15. package/dist/{colour-picker-0stHB90J.js.map → colour-picker-BSDBL-QL.js.map} +1 -1
  16. package/dist/colour-picker.js +1 -1
  17. package/dist/crumbs.js +4 -2
  18. package/dist/crumbs.js.map +1 -1
  19. package/dist/data-display.js +1 -1
  20. package/dist/data-grid.js +4 -2
  21. package/dist/data-grid.js.map +1 -1
  22. package/dist/{defaults-BCiDhZbF.js → defaults-BkWCPJFL.js} +3 -20
  23. package/dist/defaults-BkWCPJFL.js.map +1 -0
  24. package/dist/editable-label-rzXkjf1H.d.ts +75 -0
  25. package/dist/editable-label-rzXkjf1H.d.ts.map +1 -0
  26. package/dist/editable-label.d.ts +2 -75
  27. package/dist/editable-label.js +2 -1
  28. package/dist/editable-label.js.map +1 -1
  29. package/dist/{fallbacks-DEE1nCTt.js → fallbacks-DNI9xt0F.js} +2 -2
  30. package/dist/{fallbacks-DEE1nCTt.js.map → fallbacks-DNI9xt0F.js.map} +1 -1
  31. package/dist/{grouped-item-lister-_pMDwIo1.d.ts → grouped-item-lister-DnWB-eFT.d.ts} +2 -2
  32. package/dist/{grouped-item-lister-_pMDwIo1.d.ts.map → grouped-item-lister-DnWB-eFT.d.ts.map} +1 -1
  33. package/dist/grouped-item-lister.d.ts +1 -1
  34. package/dist/{icons-Bm2ByQ3s.js → icon-CvnPanhA.js} +3 -6
  35. package/dist/icon-CvnPanhA.js.map +1 -0
  36. package/dist/icons.js +8 -2
  37. package/dist/icons.js.map +1 -0
  38. package/dist/index-CG2vIgPv.d.ts +101 -0
  39. package/dist/index-CG2vIgPv.d.ts.map +1 -0
  40. package/dist/index.d.ts +655 -65
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +3682 -924
  43. package/dist/index.js.map +1 -1
  44. package/dist/interaction-D5XdKzbR.js +2 -0
  45. package/dist/labelled-radial-input.js +2 -1
  46. package/dist/labelled-radial-input.js.map +1 -1
  47. package/dist/labelled-slider-input-DzigjMwP.js +808 -0
  48. package/dist/labelled-slider-input-DzigjMwP.js.map +1 -0
  49. package/dist/labelled-slider-input.d.ts +2 -0
  50. package/dist/labelled-slider-input.js +2 -0
  51. package/dist/{types-DlVNJez_.d.ts → list-selection-types-DSuRNWpx.d.ts} +10 -2
  52. package/dist/list-selection-types-DSuRNWpx.d.ts.map +1 -0
  53. package/dist/menu-BjRvlgPy.js +825 -0
  54. package/dist/menu-BjRvlgPy.js.map +1 -0
  55. package/dist/menu-item-B-zYrTTi.js +797 -0
  56. package/dist/menu-item-B-zYrTTi.js.map +1 -0
  57. package/dist/menu.js +2 -1
  58. package/dist/miller.d.ts +1 -1
  59. package/dist/miller.js +3 -2
  60. package/dist/miller.js.map +1 -1
  61. package/dist/narrowed-text.js +4 -2
  62. package/dist/narrowed-text.js.map +1 -1
  63. package/dist/notifier.js +3 -2
  64. package/dist/notifier.js.map +1 -1
  65. package/dist/panel.d.ts +13 -13
  66. package/dist/panel.d.ts.map +1 -1
  67. package/dist/panel.js +37 -253
  68. package/dist/panel.js.map +1 -1
  69. package/dist/plots.js +1 -1
  70. package/dist/polar-pad.js +1 -1
  71. package/dist/{radial-input-Dk8Wydp5.js → radial-input-Dp5qz0ly.js} +2 -2
  72. package/dist/{radial-input-Dk8Wydp5.js.map → radial-input-Dp5qz0ly.js.map} +1 -1
  73. package/dist/radial-input.js +1 -1
  74. package/dist/range-input.js +1 -1
  75. package/dist/range.js +1 -1
  76. package/dist/registry-CkW7q09Q.js +21 -0
  77. package/dist/registry-CkW7q09Q.js.map +1 -0
  78. package/dist/select-horiz.js +3 -2
  79. package/dist/select-horiz.js.map +1 -1
  80. package/dist/{slider-input-XppH3vcy.d.ts → slider-input-BfRUW2ZB.d.ts} +16 -3
  81. package/dist/slider-input-BfRUW2ZB.d.ts.map +1 -0
  82. package/dist/{slider-input-CT4gcF-o.js → slider-input-De4eoT11.js} +382 -67
  83. package/dist/slider-input-De4eoT11.js.map +1 -0
  84. package/dist/slider-input.d.ts +1 -1
  85. package/dist/slider-input.js +1 -1
  86. package/dist/snackbar.js +1 -1
  87. package/dist/split-layout-BFSbUZQv.js +969 -0
  88. package/dist/split-layout-BFSbUZQv.js.map +1 -0
  89. package/dist/split-layout-JK3xWVX2.d.ts +179 -0
  90. package/dist/split-layout-JK3xWVX2.d.ts.map +1 -0
  91. package/dist/split-layout.d.ts +2 -60
  92. package/dist/split-layout.js +1 -617
  93. package/dist/style.css +1 -1
  94. package/dist/swipe.js +1 -1
  95. package/dist/{tab-list-D_1cYhB_.d.ts → tab-list-MPwq_1Jj.d.ts} +2 -1
  96. package/dist/tab-list-MPwq_1Jj.d.ts.map +1 -0
  97. package/dist/tabs.d.ts +1 -1
  98. package/dist/tabs.js +96 -14
  99. package/dist/tabs.js.map +1 -1
  100. package/dist/{tickled-controller-BtevrRLV.d.ts → tickled-controller-CRzdIEjJ.d.ts} +7 -1
  101. package/dist/tickled-controller-CRzdIEjJ.d.ts.map +1 -0
  102. package/dist/{tickled-controller-DkAU-0qL.js → tickled-controller-h9GmJ_bU.js} +8 -2
  103. package/dist/tickled-controller-h9GmJ_bU.js.map +1 -0
  104. package/dist/titlebar-DaCfpliv.d.ts +50 -0
  105. package/dist/titlebar-DaCfpliv.d.ts.map +1 -0
  106. package/dist/titlebar.d.ts +2 -0
  107. package/dist/titlebar.js +324 -0
  108. package/dist/titlebar.js.map +1 -0
  109. package/dist/{tree-BO6BXI2b.js → tree-Ch4W8qXQ.js} +4 -3
  110. package/dist/{tree-BO6BXI2b.js.map → tree-Ch4W8qXQ.js.map} +1 -1
  111. package/dist/tree.d.ts +1 -1
  112. package/dist/tree.js +1 -1
  113. package/dist/vertical-list-ClnqTkF3.js +814 -0
  114. package/dist/vertical-list-ClnqTkF3.js.map +1 -0
  115. package/dist/vertical-list.d.ts +8 -9
  116. package/dist/vertical-list.d.ts.map +1 -1
  117. package/dist/vertical-list.js +1 -736
  118. package/dist/{xy-axis-B6yMjckW.js → xy-axis-CxHC8z5E.js} +109 -3
  119. package/dist/xy-axis-CxHC8z5E.js.map +1 -0
  120. package/dist/xy-pad.js +1 -1
  121. package/docs-user/README.md +5 -0
  122. package/docs-user/detail-list.md +398 -0
  123. package/docs-user/dock.md +310 -0
  124. package/docs-user/index.json +27 -2
  125. package/docs-user/labelled-slider-input.md +198 -0
  126. package/docs-user/llms.txt +6 -1
  127. package/docs-user/panel.md +20 -1
  128. package/docs-user/slider-input.md +55 -15
  129. package/docs-user/split-layout.md +136 -170
  130. package/docs-user/tabs.md +34 -0
  131. package/docs-user/titlebar.md +102 -0
  132. package/docs-user/toolbar.md +95 -22
  133. package/docs-user/twosplit-layout.md +254 -0
  134. package/docs-user/user-catalog.md +8 -1
  135. package/llms.txt +6 -1
  136. package/package.json +1 -2
  137. package/dist/button-Bn0BuLpO.js.map +0 -1
  138. package/dist/defaults-BCiDhZbF.js.map +0 -1
  139. package/dist/editable-label.d.ts.map +0 -1
  140. package/dist/icons-Bm2ByQ3s.js.map +0 -1
  141. package/dist/menu-DIfU70AA.js +0 -1609
  142. package/dist/menu-DIfU70AA.js.map +0 -1
  143. package/dist/slider-input-CT4gcF-o.js.map +0 -1
  144. package/dist/slider-input-XppH3vcy.d.ts.map +0 -1
  145. package/dist/split-layout.d.ts.map +0 -1
  146. package/dist/split-layout.js.map +0 -1
  147. package/dist/tab-list-D_1cYhB_.d.ts.map +0 -1
  148. package/dist/tickled-controller-BtevrRLV.d.ts.map +0 -1
  149. package/dist/tickled-controller-DkAU-0qL.js.map +0 -1
  150. package/dist/types-DlVNJez_.d.ts.map +0 -1
  151. package/dist/vertical-list.js.map +0 -1
  152. package/dist/xy-axis-B6yMjckW.js.map +0 -1
@@ -0,0 +1,797 @@
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-CvnPanhA.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.placement = `bottom-start`;
284
+ this.tabStop = true;
285
+ this._isOpen = false;
286
+ this._hasMenu = false;
287
+ this._hasTriggerSlot = false;
288
+ this._anchorName = `--mt-${_MenuTrigger._count++}`;
289
+ this._clickListener = this._handleClickOutside.bind(this);
290
+ this._handlePopoverToggle = (e) => {
291
+ if (e.newState === `closed`) {
292
+ this._isOpen = false;
293
+ document.removeEventListener(`click`, this._clickListener);
294
+ this.dispatchEvent(new CustomEvent(`menu-trigger-close`, {
295
+ bubbles: true,
296
+ composed: true
297
+ }));
298
+ }
299
+ };
300
+ this._handleKeydown = (e) => {
301
+ if (!this._isOpen) return;
302
+ if (e.key === `Escape`) {
303
+ e.preventDefault();
304
+ e.stopPropagation();
305
+ this._closeMenu();
306
+ this._triggerBtn?.focus();
307
+ } else if (e.key === `Tab`) {
308
+ this._closeMenu();
309
+ this._triggerBtn?.focus();
310
+ }
311
+ };
312
+ }
313
+ static {
314
+ this._count = 0;
315
+ }
316
+ connectedCallback() {
317
+ super.connectedCallback();
318
+ document.addEventListener(`keydown`, this._handleKeydown);
319
+ this.addEventListener(`command`, this._handleCommand);
320
+ this.addEventListener(`radio-change`, this._handleRadioChange);
321
+ }
322
+ disconnectedCallback() {
323
+ super.disconnectedCallback();
324
+ document.removeEventListener(`keydown`, this._handleKeydown);
325
+ this.removeEventListener(`command`, this._handleCommand);
326
+ this.removeEventListener(`radio-change`, this._handleRadioChange);
327
+ document.removeEventListener(`click`, this._clickListener);
328
+ }
329
+ firstUpdated() {
330
+ this.style.setProperty(`--anchor-name`, this._anchorName);
331
+ this._popup.addEventListener(`toggle`, this._handlePopoverToggle);
332
+ this.updateComplete.then(() => {
333
+ const slot = this.shadowRoot?.querySelector(`slot[name="menu"]`);
334
+ if (slot) {
335
+ if (slot.assignedElements({ flatten: true }).length > 0) {
336
+ this._hasMenu = true;
337
+ this._propagateContext();
338
+ }
339
+ }
340
+ this._updatePosition();
341
+ });
342
+ }
343
+ updated(changedProperties) {
344
+ if (changedProperties.has(`registry`) || changedProperties.has(`keyboardManager`)) this._propagateContext();
345
+ }
346
+ _updatePosition() {
347
+ const pos = {
348
+ "bottom-start": { insetArea: `block-end span-inline-start` },
349
+ "bottom-end": { insetArea: `block-end span-inline-end` },
350
+ "right-start": { insetArea: `inline-end span-block-start` }
351
+ }[this.placement];
352
+ this._popup.style.insetArea = pos.insetArea;
353
+ this._popup.style.positionAnchor = this._anchorName;
354
+ const fb = {
355
+ "bottom-start": {
356
+ top: `anchor(bottom)`,
357
+ left: `anchor(left)`,
358
+ right: `auto`
359
+ },
360
+ "bottom-end": {
361
+ top: `anchor(bottom)`,
362
+ left: `auto`,
363
+ right: `anchor(left)`
364
+ },
365
+ "right-start": {
366
+ top: `anchor(top)`,
367
+ left: `anchor(right)`,
368
+ right: `auto`
369
+ }
370
+ }[this.placement];
371
+ this._popup.style.top = fb.top;
372
+ this._popup.style.left = fb.left;
373
+ this._popup.style.right = fb.right;
374
+ }
375
+ _propagateContext() {
376
+ const items = (this.shadowRoot?.querySelector(`slot[name="menu"]`))?.assignedElements({ flatten: true }) ?? [];
377
+ for (const el of items) {
378
+ if (`registry` in el) el.registry = this.registry;
379
+ if (`keyboardManager` in el) el.keyboardManager = this.keyboardManager;
380
+ }
381
+ }
382
+ _handleSlotChange(e) {
383
+ const assigned = e.target.assignedElements({ flatten: true });
384
+ this._hasMenu = assigned.length > 0;
385
+ this._propagateContext();
386
+ }
387
+ _handleTriggerSlotChange(e) {
388
+ const slot = e.target;
389
+ this._hasTriggerSlot = slot.assignedElements({ flatten: true }).length > 0;
390
+ }
391
+ get isOpen() {
392
+ return this._isOpen;
393
+ }
394
+ openMenu() {
395
+ this._openMenu();
396
+ }
397
+ closeMenu() {
398
+ this._closeMenu();
399
+ }
400
+ focusTrigger() {
401
+ this._triggerBtn?.focus();
402
+ }
403
+ _openMenu() {
404
+ if (this._isOpen || !this._popup?.popover) return;
405
+ try {
406
+ this._popup.showPopover({ source: this });
407
+ this._isOpen = true;
408
+ document.addEventListener(`click`, this._clickListener);
409
+ this.dispatchEvent(new CustomEvent(`menu-trigger-open`, {
410
+ bubbles: true,
411
+ composed: true
412
+ }));
413
+ requestAnimationFrame(() => {
414
+ this._getMenuContainer()?.focusFirst();
415
+ });
416
+ } catch (err) {
417
+ console.warn(`showPopover failed:`, err);
418
+ }
419
+ }
420
+ _closeMenu() {
421
+ if (!this._isOpen) return;
422
+ if (this._popup?.popover) try {
423
+ this._popup.hidePopover();
424
+ } catch {}
425
+ this._isOpen = false;
426
+ document.removeEventListener(`click`, this._clickListener);
427
+ }
428
+ _handleTriggerClick(e) {
429
+ e.stopPropagation();
430
+ if (this._isOpen) {
431
+ this._closeMenu();
432
+ this._triggerBtn?.focus();
433
+ } else this._openMenu();
434
+ }
435
+ _handleClickOutside(e) {
436
+ if (!this.contains(e.target)) {
437
+ if (this._popup?.popover && this._popup.hasAttribute(`open`)) try {
438
+ this._popup.hidePopover();
439
+ } catch {}
440
+ }
441
+ }
442
+ _getMenuContainer() {
443
+ return ((this.shadowRoot?.querySelector(`slot[name="menu"]`))?.assignedElements({ flatten: true }) ?? []).find((el) => el.tagName === `IXFX-MENU-CONTAINER`) ?? null;
444
+ }
445
+ _handleCommand(e) {
446
+ if (this._popup?.popover) try {
447
+ this._popup.hidePopover();
448
+ } catch {}
449
+ this.dispatchEvent(new CustomEvent(`menu-command`, {
450
+ detail: e.detail,
451
+ bubbles: true,
452
+ composed: true
453
+ }));
454
+ }
455
+ _handleRadioChange(e) {
456
+ if (this._popup?.popover) try {
457
+ this._popup.hidePopover();
458
+ } catch {}
459
+ this.dispatchEvent(new CustomEvent(`menu-command`, {
460
+ detail: {
461
+ command: e.detail.name,
462
+ value: e.detail.value
463
+ },
464
+ bubbles: true,
465
+ composed: true
466
+ }));
467
+ }
468
+ _handleContextMenu(e) {
469
+ if (this.mode !== `context`) return;
470
+ e.preventDefault();
471
+ this._popup.style.top = `${e.clientY}px`;
472
+ this._popup.style.left = `${e.clientX}px`;
473
+ this._popup.style.position = `fixed`;
474
+ this._popup.style.positionAnchor = `none`;
475
+ if (this._popup?.popover) try {
476
+ this._popup.showPopover();
477
+ } catch {}
478
+ }
479
+ static {
480
+ this.styles = [
481
+ themeFallbacks,
482
+ popupSurfaceStyles,
483
+ css`
484
+ :host {
485
+ display: inline-block;
486
+ position: relative;
487
+ --ixfx-button-padding: var(--ixfx-button-padding, var(--space-s) var(--space-m));
488
+ }
489
+
490
+ .trigger-area {
491
+ anchor-name: var(--anchor-name);
492
+ display: inline-flex;
493
+ align-items: stretch;
494
+ }
495
+
496
+ .trigger-btn {
497
+ background: none;
498
+ border: none;
499
+ color: var(--surface-3-text);
500
+ cursor: pointer;
501
+ padding: var(--ixfx-button-padding);
502
+ font-family: var(--font-family, inherit);
503
+ font-size: var(--text-m);
504
+ font-weight: 600;
505
+ border-radius: var(--radius-s);
506
+ opacity: 0.6;
507
+ transition: opacity var(--transition), background var(--transition);
508
+ }
509
+
510
+ .trigger-btn:hover {
511
+ background: var(--_trigger-hover-bg, var(--surface-4));
512
+ color: var(--surface-3-text);
513
+ opacity: var(--_trigger-hover-opacity, 1);
514
+ }
515
+
516
+ .trigger-btn:focus-visible {
517
+ background: var(--surface-4);
518
+ color: var(--surface-3-text);
519
+ outline: none;
520
+ opacity: 1;
521
+ }
522
+
523
+ .popup {
524
+ position: fixed;
525
+ position-anchor: var(--anchor-name);
526
+ inset: auto;
527
+ margin: 8px;
528
+ padding: 0;
529
+ border: none;
530
+ background: transparent;
531
+ z-index: 100;
532
+ max-height: min(60vh, 400px);
533
+ max-width: min(90vw, 320px);
534
+ overflow: auto;
535
+ overscroll-behavior: contain;
536
+ position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline;
537
+ }
538
+
539
+ .popup[popover] {
540
+ opacity: 1;
541
+ transform: translateY(0) scale(1);
542
+ transition: opacity 0.12s ease-out, transform 0.12s ease-out,
543
+ display 0.12s allow-discrete, overlay 0.12s allow-discrete;
544
+ }
545
+
546
+ @starting-style .popup {
547
+ opacity: 0;
548
+ transform: translateY(-4px) scale(0.97);
549
+ }
550
+ `
551
+ ];
552
+ }
553
+ render() {
554
+ const showButton = this.mode !== `dropdown` && this._hasMenu && !this._hasTriggerSlot;
555
+ return html`
556
+ <div
557
+ class="trigger-area"
558
+ @click=${this._handleTriggerClick}
559
+ @contextmenu=${this._handleContextMenu}
560
+ >
561
+ ${showButton ? html`<button class="trigger-btn" part="base" tabindex=${this.tabStop ? `0` : `-1`}>${this.label}</button>` : null}
562
+ <slot name="trigger" @slotchange=${this._handleTriggerSlotChange}></slot>
563
+ </div>
564
+ <div
565
+ class="popup popup-surface"
566
+ popover="auto"
567
+ @click=${(e) => e.stopPropagation()}
568
+ >
569
+ <slot name="menu" @slotchange=${this._handleSlotChange}></slot>
570
+ </div>
571
+ `;
572
+ }
573
+ };
574
+ __decorate([property({ type: String })], MenuTrigger.prototype, "mode", void 0);
575
+ __decorate([property({ type: String })], MenuTrigger.prototype, "label", void 0);
576
+ __decorate([property({ attribute: false })], MenuTrigger.prototype, "registry", void 0);
577
+ __decorate([property({ attribute: false })], MenuTrigger.prototype, "keyboardManager", void 0);
578
+ __decorate([property({ type: String })], MenuTrigger.prototype, "placement", void 0);
579
+ __decorate([property({
580
+ type: Boolean,
581
+ attribute: `tab-stop`
582
+ })], MenuTrigger.prototype, "tabStop", void 0);
583
+ __decorate([state()], MenuTrigger.prototype, "_isOpen", void 0);
584
+ __decorate([state()], MenuTrigger.prototype, "_hasMenu", void 0);
585
+ __decorate([state()], MenuTrigger.prototype, "_hasTriggerSlot", void 0);
586
+ __decorate([query(`.trigger-btn`)], MenuTrigger.prototype, "_triggerBtn", void 0);
587
+ __decorate([query(`.popup`)], MenuTrigger.prototype, "_popup", void 0);
588
+ MenuTrigger = _MenuTrigger = __decorate([safeCustomElement(`ixfx-menu-trigger`)], MenuTrigger);
589
+ //#endregion
590
+ //#region src/menu/menu-item.ts
591
+ let MenuItem = class MenuItem extends LitElement {
592
+ constructor(..._args) {
593
+ super(..._args);
594
+ this.command = ``;
595
+ this.commandArgs = ``;
596
+ this.label = ``;
597
+ this.disabled = false;
598
+ this.iconName = ``;
599
+ this._hasChildren = false;
600
+ this._hoverTimeout = null;
601
+ }
602
+ disconnectedCallback() {
603
+ super.disconnectedCallback();
604
+ if (this._hoverTimeout) clearTimeout(this._hoverTimeout);
605
+ }
606
+ _handleSlotChange() {
607
+ const assigned = this._slot?.assignedElements() ?? [];
608
+ this._hasChildren = assigned.length > 0;
609
+ }
610
+ _handleMouseEnter() {
611
+ if (this.disabled || !this._hasChildren || !this.submenuPopover) return;
612
+ if (this._hoverTimeout) clearTimeout(this._hoverTimeout);
613
+ this._hoverTimeout = window.setTimeout(() => {
614
+ this._showSubmenu();
615
+ }, 150);
616
+ }
617
+ _handleMouseLeave() {
618
+ if (this._hoverTimeout) {
619
+ clearTimeout(this._hoverTimeout);
620
+ this._hoverTimeout = null;
621
+ }
622
+ this._hideSubmenu();
623
+ }
624
+ _showSubmenu() {
625
+ if (!this.submenuPopover?.popover) return;
626
+ try {
627
+ this.submenuPopover.showPopover();
628
+ } catch (err) {
629
+ console.warn(`showPopover failed:`, err);
630
+ }
631
+ }
632
+ _hideSubmenu() {
633
+ if (!this.submenuPopover?.popover) return;
634
+ try {
635
+ this.submenuPopover.hidePopover();
636
+ } catch (err) {
637
+ console.warn(`hidePopover failed:`, err);
638
+ }
639
+ }
640
+ handleClick() {
641
+ if (this.disabled) return;
642
+ this.dispatchEvent(new CustomEvent(`command`, {
643
+ detail: {
644
+ command: this.command,
645
+ args: this.commandArgs
646
+ },
647
+ bubbles: true,
648
+ composed: true
649
+ }));
650
+ }
651
+ _resolvedLabel() {
652
+ if (this.label) return this.label;
653
+ if (this.registry && this.command) return this.registry.get(this.command)?.label ?? ``;
654
+ return ``;
655
+ }
656
+ _resolvedIcon() {
657
+ if (this.iconName) return this.iconName;
658
+ if (this.registry && this.command) return this.registry.get(this.command)?.icon ?? null;
659
+ return null;
660
+ }
661
+ willUpdate(changedProperties) {
662
+ super.willUpdate(changedProperties);
663
+ const resolvedIcon = this._resolvedIcon();
664
+ if (resolvedIcon && !this.iconName) this.setAttribute(`data-icon-section`, ``);
665
+ else if (!resolvedIcon && !this.iconName) this.removeAttribute(`data-icon-section`);
666
+ }
667
+ updated(changedProperties) {
668
+ super.updated(changedProperties);
669
+ }
670
+ _getShortcut() {
671
+ if (!this.keyboardManager || !this.command) return null;
672
+ const binding = (this.keyboardManager.getBindings?.() ?? []).find((b) => b.commandId === this.command);
673
+ if (!binding) return null;
674
+ const mods = Array.from(binding.modifiers ?? [], (m) => ({
675
+ ctrl: `⌃`,
676
+ shift: `⇧`,
677
+ alt: `⌥`,
678
+ meta: `⌘`
679
+ })[m] ?? m);
680
+ const key = binding.key?.length === 1 ? binding.key.toUpperCase() : binding.key;
681
+ return [...mods, key].join(``);
682
+ }
683
+ static {
684
+ this.styles = [
685
+ themeFallbacks,
686
+ tickledItemStyles,
687
+ css`
688
+ :host {
689
+ display: block;
690
+ }
691
+
692
+ .item {
693
+ display: flex !important;
694
+ align-items: center;
695
+ padding: 6px var(--space-m);
696
+ font-size: var(--text-m);
697
+ background: var(--item-bg, transparent);
698
+ border: var(--item-border, 1px solid transparent);
699
+ color: var(--item-text, inherit);
700
+ }
701
+
702
+ :host(.tickled) .item {
703
+ background: var(--item-bg-tickled);
704
+ border: var(--item-border-tickled);
705
+ color: var(--item-text-tickled);
706
+ }
707
+
708
+ :host(:focus) {
709
+ outline: none;
710
+ }
711
+
712
+ .item.disabled {
713
+ opacity: var(--disabled-opacity);
714
+ cursor: default;
715
+ }
716
+
717
+ .item.disabled:hover {
718
+ background: transparent;
719
+ }
720
+
721
+ .icon-slot {
722
+ width: 0;
723
+ overflow: hidden;
724
+ flex-shrink: 0;
725
+ display: inline-flex;
726
+ align-items: center;
727
+ justify-content: center;
728
+ }
729
+
730
+ :host([icon-name]) .icon-slot,
731
+ :host([data-icon-section]) .icon-slot {
732
+ width: 1em;
733
+ height: 1em;
734
+ margin-right: var(--space-s);
735
+ }
736
+
737
+ .label {
738
+ flex: 1;
739
+ }
740
+
741
+ .shortcut {
742
+ margin-left: auto;
743
+ color: var(--surface-muted-text);
744
+ font-size: var(--text-s);
745
+ }
746
+
747
+ .arrow {
748
+ color: var(--surface-muted-text);
749
+ margin-left: var(--space-m);
750
+ }
751
+ `
752
+ ];
753
+ }
754
+ render() {
755
+ const shortcut = this._getShortcut();
756
+ const resolvedIcon = this._resolvedIcon();
757
+ return html`
758
+ <div
759
+ class="item ${this.disabled ? `disabled` : ``}"
760
+ @click=${this.handleClick}
761
+ @mouseenter=${this._handleMouseEnter}
762
+ @mouseleave=${this._handleMouseLeave}
763
+ tabindex="${this.disabled ? -1 : 0}"
764
+ role="menuitem"
765
+ aria-haspopup="${this._hasChildren ? `true` : `false`}"
766
+ aria-expanded="${this.submenuPopover?.matches(`:popover-open`) ?? false}"
767
+ >
768
+ <span class="icon-slot">${resolvedIcon ? html`<ixfx-icon name="${resolvedIcon}"></ixfx-icon>` : null}</span>
769
+ <span class="label"><slot></slot>${this._resolvedLabel()}</span>
770
+ ${shortcut ? html`<span class="shortcut">${shortcut}</span>` : null}
771
+ ${this._hasChildren ? html`<span class="arrow"><ixfx-icon name="chevron-right"></ixfx-icon></span>` : null}
772
+ </div>
773
+ <slot @slotchange=${this._handleSlotChange}></slot>
774
+ `;
775
+ }
776
+ };
777
+ __decorate([property({ type: String })], MenuItem.prototype, "command", void 0);
778
+ __decorate([property({
779
+ type: String,
780
+ attribute: `command-args`
781
+ })], MenuItem.prototype, "commandArgs", void 0);
782
+ __decorate([property({ type: String })], MenuItem.prototype, "label", void 0);
783
+ __decorate([property({ type: Boolean })], MenuItem.prototype, "disabled", void 0);
784
+ __decorate([property({
785
+ type: String,
786
+ attribute: `icon-name`
787
+ })], MenuItem.prototype, "iconName", void 0);
788
+ __decorate([property({ attribute: false })], MenuItem.prototype, "registry", void 0);
789
+ __decorate([property({ attribute: false })], MenuItem.prototype, "keyboardManager", void 0);
790
+ __decorate([property({ attribute: false })], MenuItem.prototype, "submenuPopover", void 0);
791
+ __decorate([state()], MenuItem.prototype, "_hasChildren", void 0);
792
+ __decorate([query(`slot`)], MenuItem.prototype, "_slot", void 0);
793
+ MenuItem = __decorate([safeCustomElement(`ixfx-menu-item`)], MenuItem);
794
+ //#endregion
795
+ export { MenuTrigger as n, MenuContainer as r, MenuItem as t };
796
+
797
+ //# sourceMappingURL=menu-item-B-zYrTTi.js.map