@ixfx/components 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundle/index.d.ts +1086 -603
- package/bundle/index.d.ts.map +1 -1
- package/bundle/index.js +16527 -14258
- package/bundle/index.js.map +1 -1
- package/bundle/style.css +14 -4
- package/dist/ac-text.d.ts.map +1 -1
- package/dist/ac-text.js +11 -6
- package/dist/ac-text.js.map +1 -1
- package/dist/{button-DMt5XWUK.js → button-C3fLMKUv.js} +4 -4
- package/dist/{button-DMt5XWUK.js.map → button-C3fLMKUv.js.map} +1 -1
- package/dist/button.d.ts.map +1 -1
- package/dist/button.js +1 -1
- package/dist/checkbox.d.ts +2 -2
- package/dist/checkbox.d.ts.map +1 -1
- package/dist/checkbox.js +14 -8
- package/dist/checkbox.js.map +1 -1
- package/dist/{colour-C3MQIjFJ-BLXg8W3U.js → colour-C3MQIjFJ-vRCdr8W-.js} +146 -552
- package/dist/colour-C3MQIjFJ-vRCdr8W-.js.map +1 -0
- package/dist/{colour-picker-K6zu82lm.js → colour-picker-0sgAha1T.js} +14 -9
- package/dist/colour-picker-0sgAha1T.js.map +1 -0
- package/dist/colour-picker.d.ts +1 -1
- package/dist/colour-picker.js +1 -1
- package/dist/crumbs.d.ts +1 -1
- package/dist/crumbs.d.ts.map +1 -1
- package/dist/crumbs.js +11 -6
- package/dist/crumbs.js.map +1 -1
- package/dist/data-display.d.ts +9 -4
- package/dist/data-display.d.ts.map +1 -1
- package/dist/data-display.js +270 -43
- package/dist/data-display.js.map +1 -1
- package/dist/data-provider-DgI909FP.js +23 -0
- package/dist/data-provider-DgI909FP.js.map +1 -0
- package/dist/{decorate-D7rC1gLP.js → decorate-DdjvVHS-.js} +2 -2
- package/dist/{decorate-D7rC1gLP.js.map → decorate-DdjvVHS-.js.map} +1 -1
- package/dist/{defaults-C7elhuyJ.js → defaults-C3b9OWJD.js} +2 -2
- package/dist/{defaults-C7elhuyJ.js.map → defaults-C3b9OWJD.js.map} +1 -1
- package/dist/{dist-TAGYwaju.js → dist-CA13FvMn.js} +7 -66
- package/dist/dist-CA13FvMn.js.map +1 -0
- package/dist/editable-label.d.ts.map +1 -1
- package/dist/editable-label.js +3 -2
- package/dist/editable-label.js.map +1 -1
- package/dist/fallbacks-pNMOMx38.js +168 -0
- package/dist/fallbacks-pNMOMx38.js.map +1 -0
- package/dist/{hex-editor-DlfoA65y.d.ts → hex-editor-CMIxtNgT.d.ts} +1 -1
- package/dist/hex-editor-CMIxtNgT.d.ts.map +1 -0
- package/dist/{hex-editor-CsN_ySkH.js → hex-editor-WZrjRtvL.js} +3 -3
- package/dist/{hex-editor-CsN_ySkH.js.map → hex-editor-WZrjRtvL.js.map} +1 -1
- package/dist/hex.d.ts +1 -1
- package/dist/hex.js +1 -1
- package/dist/{highlight-TPbSHruI.js → highlight-B-eQrhhb.js} +1 -1
- package/dist/{highlight-TPbSHruI.js.map → highlight-B-eQrhhb.js.map} +1 -1
- package/dist/{icon-CeTDJhC7.d.ts → icon-Bd5BiU4b.d.ts} +2 -2
- package/dist/icon-Bd5BiU4b.d.ts.map +1 -0
- package/dist/{icon-9NWTXtE1.js → icon-Ndo40kNO.js} +6 -5
- package/dist/icon-Ndo40kNO.js.map +1 -0
- package/dist/icons.d.ts +2 -2
- package/dist/icons.js +3 -3
- package/dist/{incr-search-DSKKsk6w.js → incr-search-CeERfijK.js} +2 -2
- package/dist/{incr-search-DSKKsk6w.js.map → incr-search-CeERfijK.js.map} +1 -1
- package/dist/incr-search.d.ts +1 -1
- package/dist/incr-search.js +2 -2
- package/dist/{index-hXFn14tM.d.ts → index-BMjri2-S.d.ts} +2 -2
- package/dist/{index-hXFn14tM.d.ts.map → index-BMjri2-S.d.ts.map} +1 -1
- package/dist/{index-DSlPeWLK.d.ts → index-CWpNcQKE.d.ts} +5 -5
- package/dist/index-CWpNcQKE.d.ts.map +1 -0
- package/dist/{index--hrxPBkQ.d.ts → index-CowKi2mo.d.ts} +2 -1
- package/dist/index-CowKi2mo.d.ts.map +1 -0
- package/dist/{index-B1hNR7Z9.d.ts → index-D87kRcEs.d.ts} +1 -1
- package/dist/{index-B1hNR7Z9.d.ts.map → index-D87kRcEs.d.ts.map} +1 -1
- package/dist/{index-CnL9krsH.d.ts → index-DgXpCg_G.d.ts} +32 -26
- package/dist/index-DgXpCg_G.d.ts.map +1 -0
- package/dist/{index-DrQjA8Gs.d.ts → index-s_IhSnwc.d.ts} +2 -2
- package/dist/index-s_IhSnwc.d.ts.map +1 -0
- package/dist/index.d.ts +432 -121
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2370 -1707
- package/dist/index.js.map +1 -1
- package/dist/interaction-D6efrBLo.js +1 -0
- package/dist/{keyboard-De_vOHLg.js → keyboard-CimiEskD.js} +1 -1
- package/dist/{keyboard-De_vOHLg.js.map → keyboard-CimiEskD.js.map} +1 -1
- package/dist/{labelled-input-base-CqPL5rKm.js → labelled-input-base-9K_w3oLj.js} +2 -2
- package/dist/{labelled-input-base-CqPL5rKm.js.map → labelled-input-base-9K_w3oLj.js.map} +1 -1
- package/dist/{labelled-input-base-D-Y7agky.d.ts → labelled-input-base-ORb1rGq6.d.ts} +1 -1
- package/dist/labelled-input-base-ORb1rGq6.d.ts.map +1 -0
- package/dist/labelled-radial-input.d.ts +2 -2
- package/dist/labelled-radial-input.d.ts.map +1 -1
- package/dist/labelled-radial-input.js +7 -6
- package/dist/labelled-radial-input.js.map +1 -1
- package/dist/labelled-range-input.d.ts +1 -1
- package/dist/labelled-range-input.js +2 -2
- package/dist/led.d.ts +1 -1
- package/dist/led.d.ts.map +1 -1
- package/dist/led.js +2 -2
- package/dist/led.js.map +1 -1
- package/dist/{menu-item-CKRfP0pN.js → menu-item-BrIuORYD.js} +20 -10
- package/dist/menu-item-BrIuORYD.js.map +1 -0
- package/dist/{menu-item-ClQF2DKN.d.ts → menu-item-COx-Azgj.d.ts} +1 -1
- package/dist/menu-item-COx-Azgj.d.ts.map +1 -0
- package/dist/{menu-DJJuCm-5.js → menu-vjoiHZ13.js} +30 -17
- package/dist/menu-vjoiHZ13.js.map +1 -0
- package/dist/menu.d.ts +2 -2
- package/dist/menu.js +2 -2
- package/dist/miller.d.ts +2 -2
- package/dist/miller.d.ts.map +1 -1
- package/dist/miller.js +18 -11
- package/dist/miller.js.map +1 -1
- package/dist/narrowed-text.d.ts.map +1 -1
- package/dist/narrowed-text.js +7 -5
- package/dist/narrowed-text.js.map +1 -1
- package/dist/panel.d.ts +1 -1
- package/dist/panel.d.ts.map +1 -1
- package/dist/panel.js +5 -4
- package/dist/panel.js.map +1 -1
- package/dist/plots.d.ts +2 -2
- package/dist/plots.js +2 -2
- package/dist/polar-pad.d.ts +1 -1
- package/dist/polar-pad.d.ts.map +1 -1
- package/dist/polar-pad.js +10 -4
- package/dist/polar-pad.js.map +1 -1
- package/dist/{popup-ImtS9KnS.js → popup-DiWMxsWj.js} +1 -1
- package/dist/{popup-ImtS9KnS.js.map → popup-DiWMxsWj.js.map} +1 -1
- package/dist/prominence-DlFShic3.js +104 -0
- package/dist/prominence-DlFShic3.js.map +1 -0
- package/dist/{radial-input-LUSBEycd.d.ts → radial-input-CpQPs1Mp.d.ts} +1 -1
- package/dist/radial-input-CpQPs1Mp.d.ts.map +1 -0
- package/dist/{radial-input-DZA6WaFu.js → radial-input-DZ7eQCcM.js} +11 -6
- package/dist/radial-input-DZ7eQCcM.js.map +1 -0
- package/dist/radial-input.d.ts +1 -1
- package/dist/radial-input.js +1 -1
- package/dist/range-input.d.ts +1 -1
- package/dist/range-input.d.ts.map +1 -1
- package/dist/range-input.js +9 -4
- package/dist/range-input.js.map +1 -1
- package/dist/range.d.ts +1 -1
- package/dist/range.d.ts.map +1 -1
- package/dist/range.js +5 -3
- package/dist/range.js.map +1 -1
- package/dist/{registry-Q2gYQCHM.js → registry-CkW7q09Q.js} +1 -1
- package/dist/{registry-Q2gYQCHM.js.map → registry-CkW7q09Q.js.map} +1 -1
- package/dist/selecthorizontal.d.ts +1 -1
- package/dist/selecthorizontal.d.ts.map +1 -1
- package/dist/selecthorizontal.js +4 -3
- package/dist/selecthorizontal.js.map +1 -1
- package/dist/snap-container.d.ts.map +1 -1
- package/dist/snap-container.js +1 -1
- package/dist/split-layout.d.ts +1 -1
- package/dist/split-layout.d.ts.map +1 -1
- package/dist/split-layout.js +6 -5
- package/dist/split-layout.js.map +1 -1
- package/dist/style.css +14 -4
- package/dist/swipe.d.ts.map +1 -1
- package/dist/swipe.js +9 -4
- package/dist/swipe.js.map +1 -1
- package/dist/{tab-list-BnqISNvO.d.ts → tab-list-DLzb-m-I.d.ts} +2 -2
- package/dist/tab-list-DLzb-m-I.d.ts.map +1 -0
- package/dist/tabs.d.ts +1 -1
- package/dist/tabs.js +10 -4
- package/dist/tabs.js.map +1 -1
- package/dist/{tickled-controller-Cq1Va0rR.d.ts → tickled-controller-BjYCzGrU.d.ts} +2 -2
- package/dist/tickled-controller-BjYCzGrU.d.ts.map +1 -0
- package/dist/{tickled-styles-Bg3QbcrD.js → tickled-styles-fNDdqf6l.js} +1 -17
- package/dist/{tickled-styles-Bg3QbcrD.js.map → tickled-styles-fNDdqf6l.js.map} +1 -1
- package/dist/{timeline-Cv75Th2U.js → timeline-2OFtN4Ta.js} +1437 -36
- package/dist/timeline-2OFtN4Ta.js.map +1 -0
- package/dist/timeline.d.ts +1 -1
- package/dist/timeline.js +1 -1
- package/dist/{tooltip-DMxmBNky.js → tooltip-CJt3WLmN.js} +1 -1
- package/dist/{tooltip-DMxmBNky.js.map → tooltip-CJt3WLmN.js.map} +1 -1
- package/dist/{tooltip-CwGfb4lp.d.ts → tooltip-RGVIwHrZ.d.ts} +1 -1
- package/dist/{tooltip-CwGfb4lp.d.ts.map → tooltip-RGVIwHrZ.d.ts.map} +1 -1
- package/dist/{tree-CvvKbO-f.js → tree-CJWpMUvD.js} +113 -103
- package/dist/tree-CJWpMUvD.js.map +1 -0
- package/dist/{tree-component-D8Bg48tt.d.ts → tree-component-BcO26Xvt.d.ts} +65 -3
- package/dist/tree-component-BcO26Xvt.d.ts.map +1 -0
- package/dist/tree.d.ts +15 -15
- package/dist/tree.d.ts.map +1 -1
- package/dist/tree.js +1 -1
- package/dist/{types-BKaqnpwx.d.ts → types-DSfbtHs3.d.ts} +1 -1
- package/dist/types-DSfbtHs3.d.ts.map +1 -0
- package/dist/{xy-axis-DL0tfJDr.js → xy-axis-CK64haaK.js} +1831 -359
- package/dist/xy-axis-CK64haaK.js.map +1 -0
- package/dist/{xy-axis-D9uwpGTT.d.ts → xy-axis-McdUpcYr.d.ts} +102 -3
- package/dist/xy-axis-McdUpcYr.d.ts.map +1 -0
- package/dist/xy-pad.d.ts +1 -1
- package/dist/xy-pad.d.ts.map +1 -1
- package/dist/xy-pad.js +11 -6
- package/dist/xy-pad.js.map +1 -1
- package/docs-user/README.md +45 -0
- package/docs-user/ac-text.md +67 -0
- package/docs-user/ac-token.md +187 -0
- package/docs-user/button.md +355 -0
- package/docs-user/checkbox.md +62 -0
- package/docs-user/colour-picker.md +237 -0
- package/docs-user/crumbs.md +66 -0
- package/docs-user/data-display.md +98 -0
- package/docs-user/editable-label.md +244 -0
- package/docs-user/grouped-item-lister.md +424 -0
- package/docs-user/icons.md +191 -0
- package/docs-user/incr-search.md +94 -0
- package/docs-user/labelled-radial-input.md +204 -0
- package/docs-user/labelled-range-input.md +194 -0
- package/docs-user/led.md +40 -0
- package/docs-user/menu.md +319 -0
- package/docs-user/miller.md +453 -0
- package/docs-user/narrowed-text.md +72 -0
- package/docs-user/panel.md +250 -0
- package/docs-user/plots.md +361 -0
- package/docs-user/polar-pad.md +228 -0
- package/docs-user/radial-input.md +559 -0
- package/docs-user/range-input.md +264 -0
- package/docs-user/range.md +12 -0
- package/docs-user/selecthorizontal.md +32 -0
- package/docs-user/snap-container.md +132 -0
- package/docs-user/split-layout.md +232 -0
- package/docs-user/swipe.md +44 -0
- package/docs-user/tabs.md +217 -0
- package/docs-user/timeline.md +116 -0
- package/docs-user/transitory-label.md +120 -0
- package/docs-user/tree.md +592 -0
- package/docs-user/user-catalog.md +165 -0
- package/docs-user/user-theming.md +379 -0
- package/docs-user/util.md +122 -0
- package/docs-user/vertical-list.md +351 -0
- package/docs-user/xy-pad.md +94 -0
- package/package.json +19 -18
- package/dist/colour-C3MQIjFJ-BLXg8W3U.js.map +0 -1
- package/dist/colour-picker-K6zu82lm.js.map +0 -1
- package/dist/dist-BX0OVIm7.js +0 -1290
- package/dist/dist-BX0OVIm7.js.map +0 -1
- package/dist/dist-TAGYwaju.js.map +0 -1
- package/dist/hex-editor-DlfoA65y.d.ts.map +0 -1
- package/dist/icon-9NWTXtE1.js.map +0 -1
- package/dist/icon-CeTDJhC7.d.ts.map +0 -1
- package/dist/index--hrxPBkQ.d.ts.map +0 -1
- package/dist/index-CnL9krsH.d.ts.map +0 -1
- package/dist/index-DSlPeWLK.d.ts.map +0 -1
- package/dist/index-DrQjA8Gs.d.ts.map +0 -1
- package/dist/interaction-DG7X1rhI.js +0 -1
- package/dist/labelled-input-base-D-Y7agky.d.ts.map +0 -1
- package/dist/menu-DJJuCm-5.js.map +0 -1
- package/dist/menu-item-CKRfP0pN.js.map +0 -1
- package/dist/menu-item-ClQF2DKN.d.ts.map +0 -1
- package/dist/prominence--ic9uWBl.js +0 -98
- package/dist/prominence--ic9uWBl.js.map +0 -1
- package/dist/radial-input-DZA6WaFu.js.map +0 -1
- package/dist/radial-input-LUSBEycd.d.ts.map +0 -1
- package/dist/tab-list-BnqISNvO.d.ts.map +0 -1
- package/dist/tickled-controller-Cq1Va0rR.d.ts.map +0 -1
- package/dist/timeline-Cv75Th2U.js.map +0 -1
- package/dist/tree-CvvKbO-f.js.map +0 -1
- package/dist/tree-component-D8Bg48tt.d.ts.map +0 -1
- package/dist/types-BKaqnpwx.d.ts.map +0 -1
- package/dist/xy-axis-D9uwpGTT.d.ts.map +0 -1
- package/dist/xy-axis-DL0tfJDr.js.map +0 -1
- /package/bundle/{chunk-pbuEa-1d.js → chunk-D7D4PA-g.js} +0 -0
- /package/dist/{chunk-pbuEa-1d.js → chunk-D7D4PA-g.js} +0 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
# Menu Infrastructure
|
|
2
|
+
|
|
3
|
+
A custom menu system built on native HTML popovers with keyboard navigation, tickled focus management, and command routing.
|
|
4
|
+
|
|
5
|
+
## Components
|
|
6
|
+
|
|
7
|
+
| Component | Description |
|
|
8
|
+
|------------|-------------|
|
|
9
|
+
| `ixfx-menu-bar` | Horizontal container for menu triggers; handles arrow-key navigation between top-level menus |
|
|
10
|
+
| `ixfx-menu-trigger` | Button or slot that opens a menu popover. Modes: `trigger` (button), `dropdown` (slot-only), `context` (right-click) |
|
|
11
|
+
| `ixfx-menu-container` | The popover panel holding menu items; manages keyboard focus and tickled state |
|
|
12
|
+
| `ixfx-menu-item` | Basic menu item that dispatches a `command` event |
|
|
13
|
+
| `ixfx-menu-check-item` | Toggle checkbox item; dispatches `command` with `checked` state |
|
|
14
|
+
| `ixfx-menu-radio-item` | Radio button item for use inside `ixfx-menu-radio-group` |
|
|
15
|
+
| `ixfx-menu-radio-group` | Groups radio items; dispatches `radio-change` when selection changes |
|
|
16
|
+
| `ixfx-menu-sub-trigger` | Menu item that reveals a nested submenu on hover or ArrowRight |
|
|
17
|
+
| `ixfx-menu-separator` | Visual divider between menu sections |
|
|
18
|
+
| `ixfx-menu-header` | Section label header |
|
|
19
|
+
|
|
20
|
+
## Architecture
|
|
21
|
+
|
|
22
|
+
Menus are built on the native HTML `<popover>` API. `ixfx-menu-trigger` owns a `<popover>` element and uses `showPopover()`/`hidePopover()` to open/close it. `ixfx-menu-container` is slotted into the popover and handles:
|
|
23
|
+
|
|
24
|
+
- **Tickled focus management** — tracks which item should be focused based on keyboard vs mouse input
|
|
25
|
+
- **Keyboard navigation** — Arrow keys, Home/End, Enter/Space to activate
|
|
26
|
+
- **Icon section alignment** — detects whether any item in a section has an icon and pads all items consistently
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
### Declarative Style
|
|
31
|
+
|
|
32
|
+
Static menu markup in HTML:
|
|
33
|
+
|
|
34
|
+
```html
|
|
35
|
+
<ixfx-menu-bar>
|
|
36
|
+
<ixfx-menu-trigger label="File" @menu-command=${handleCommand}>
|
|
37
|
+
<ixfx-menu-container slot="menu">
|
|
38
|
+
<ixfx-menu-item command="new" label="New"></ixfx-menu-item>
|
|
39
|
+
<ixfx-menu-item command="open" label="Open"></ixfx-menu-item>
|
|
40
|
+
<ixfx-menu-separator></ixfx-menu-separator>
|
|
41
|
+
<ixfx-menu-item command="exit" label="Exit"></ixfx-menu-item>
|
|
42
|
+
</ixfx-menu-container>
|
|
43
|
+
</ixfx-menu-trigger>
|
|
44
|
+
|
|
45
|
+
<ixfx-menu-trigger label="Edit" @menu-command=${handleCommand}>
|
|
46
|
+
<ixfx-menu-container slot="menu">
|
|
47
|
+
<ixfx-menu-check-item command="bold" label="Bold" checked></ixfx-menu-check-item>
|
|
48
|
+
<ixfx-menu-check-item command="italic" label="Italic"></ixfx-menu-check-item>
|
|
49
|
+
</ixfx-menu-container>
|
|
50
|
+
</ixfx-menu-trigger>
|
|
51
|
+
</ixfx-menu-bar>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Imperative Style
|
|
55
|
+
|
|
56
|
+
Building menus programmatically via Lit templates:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import { html } from 'lit';
|
|
60
|
+
import { MenuItem } from './menu/menu-item.js';
|
|
61
|
+
import { MenuContainer } from './menu/menu-container.js';
|
|
62
|
+
import { MenuTrigger } from './menu/menu-trigger.js';
|
|
63
|
+
import { MenuSeparator } from './menu/menu-separator.js';
|
|
64
|
+
import { MenuCheckItem } from './menu/menu-check-item.js';
|
|
65
|
+
|
|
66
|
+
function buildEditMenu(onCommand: (cmd: string) => void) {
|
|
67
|
+
return html`
|
|
68
|
+
<ixfx-menu-container slot="menu">
|
|
69
|
+
<ixfx-menu-check-item
|
|
70
|
+
command="bold"
|
|
71
|
+
label="Bold"
|
|
72
|
+
@command=${(e: CustomEvent) => onCommand(e.detail.command)}
|
|
73
|
+
></ixfx-menu-check-item>
|
|
74
|
+
<ixfx-menu-check-item
|
|
75
|
+
command="italic"
|
|
76
|
+
label="Italic"
|
|
77
|
+
@command=${(e: CustomEvent) => onCommand(e.detail.command)}
|
|
78
|
+
></ixfx-menu-check-item>
|
|
79
|
+
</ixfx-menu-container>
|
|
80
|
+
`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const trigger = document.createElement('ixfx-menu-trigger');
|
|
84
|
+
trigger.label = 'Edit';
|
|
85
|
+
trigger.addEventListener('menu-command', (e) => console.log(e.detail));
|
|
86
|
+
trigger.appendChild(buildEditMenu(myHandler));
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Building Menus from TreeNode Structure
|
|
90
|
+
|
|
91
|
+
Use `menuFromTree()` or `menuFromTreeToElements()` to construct menus from a `TreeNode[]` array:
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import { menuFromTree, menuFromTreeToElements } from './menu/menu-from-tree.js';
|
|
95
|
+
import type { TreeNode } from './tree/types.js';
|
|
96
|
+
|
|
97
|
+
const nodes: TreeNode[] = [
|
|
98
|
+
{ item: { key: 'new', label: 'New' } },
|
|
99
|
+
{ item: { key: 'open', label: 'Open...' } },
|
|
100
|
+
{ item: { key: 'sep1', label: '-' } }, // becomes separator
|
|
101
|
+
{ item: { key: 'sep2', label: '# File' } }, // becomes header "File"
|
|
102
|
+
{
|
|
103
|
+
item: { key: 'submenu', label: 'Submenu' },
|
|
104
|
+
children: [
|
|
105
|
+
{ item: { key: 'action1', label: 'Action 1' } },
|
|
106
|
+
{ item: { key: 'action2', label: 'Action 2' } },
|
|
107
|
+
],
|
|
108
|
+
},
|
|
109
|
+
{ item: { key: 'quit', label: 'Quit' } },
|
|
110
|
+
];
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Rules:**
|
|
114
|
+
- `label === "-"` → separator
|
|
115
|
+
- `label.startsWith("# ")` → header (prefix stripped)
|
|
116
|
+
- Node has `children` → submenu trigger (no command)
|
|
117
|
+
- Leaf node → menu item with `command = item.key`
|
|
118
|
+
|
|
119
|
+
**In Lit templates** (declarative):
|
|
120
|
+
```ts
|
|
121
|
+
// Inside a Lit component's render()
|
|
122
|
+
return html`
|
|
123
|
+
<ixfx-menu-trigger label="File">
|
|
124
|
+
<ixfx-menu-container slot="menu">
|
|
125
|
+
${menuFromTree(nodes, { onCommand: this.handleCommand, registry: this.registry })}
|
|
126
|
+
</ixfx-menu-container>
|
|
127
|
+
</ixfx-menu-trigger>
|
|
128
|
+
`;
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Imperatively** (using DOM):
|
|
132
|
+
```ts
|
|
133
|
+
const elements = menuFromTreeToElements(nodes, { onCommand: handleCommand });
|
|
134
|
+
for (const el of elements) {
|
|
135
|
+
container.appendChild(el);
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Programmatic Open/Close
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
const trigger = document.querySelector('ixfx-menu-trigger');
|
|
143
|
+
|
|
144
|
+
// Open
|
|
145
|
+
trigger.openMenu();
|
|
146
|
+
|
|
147
|
+
// Close
|
|
148
|
+
trigger.closeMenu();
|
|
149
|
+
|
|
150
|
+
// Check state
|
|
151
|
+
console.log(trigger.isOpen);
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Submenus
|
|
155
|
+
|
|
156
|
+
Use `ixfx-menu-sub-trigger` with a nested `ixfx-menu-container` in its `submenu` slot:
|
|
157
|
+
|
|
158
|
+
```html
|
|
159
|
+
<ixfx-menu-item command="view" label="View">
|
|
160
|
+
<ixfx-menu-container slot="submenu">
|
|
161
|
+
<ixfx-menu-item command="zoom-in" label="Zoom In"></ixfx-menu-item>
|
|
162
|
+
<ixfx-menu-item command="zoom-out" label="Zoom Out"></ixfx-menu-item>
|
|
163
|
+
</ixfx-menu-container>
|
|
164
|
+
</ixfx-menu-item>
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Radio Groups
|
|
168
|
+
|
|
169
|
+
```html
|
|
170
|
+
<ixfx-menu-radio-group name="alignment" value="left" @radio-change=${handleAlign}>
|
|
171
|
+
<ixfx-menu-radio-item value="left" label="Left"></ixfx-menu-radio-item>
|
|
172
|
+
<ixfx-menu-radio-item value="center" label="Center"></ixfx-menu-radio-item>
|
|
173
|
+
<ixfx-menu-radio-item value="right" label="Right"></ixfx-menu-radio-item>
|
|
174
|
+
</ixfx-menu-radio-group>
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Context Menu Mode
|
|
178
|
+
|
|
179
|
+
Set `mode="context"` on the trigger. The menu appears at the cursor position on right-click:
|
|
180
|
+
|
|
181
|
+
```html
|
|
182
|
+
<ixfx-menu-trigger mode="context" @menu-command=${handleContextMenu}>
|
|
183
|
+
<ixfx-menu-container slot="menu">
|
|
184
|
+
<ixfx-menu-item command="cut" label="Cut"></ixfx-menu-item>
|
|
185
|
+
<ixfx-menu-item command="copy" label="Copy"></ixfx-menu-item>
|
|
186
|
+
<ixfx-menu-item command="paste" label="Paste"></ixfx-menu-item>
|
|
187
|
+
</ixfx-menu-container>
|
|
188
|
+
</ixfx-menu-trigger>
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Command Registry Integration
|
|
192
|
+
|
|
193
|
+
Pass a `CommandRegistry` to the trigger and/or container. Items resolve their label and icon from the registry if not explicitly set:
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
import { CommandRegistry } from '@clinth/ui-commands';
|
|
197
|
+
|
|
198
|
+
const registry = new CommandRegistry();
|
|
199
|
+
registry.register('bold', { label: 'Bold', icon: 'bold' });
|
|
200
|
+
registry.register('italic', { label: 'Italic', icon: 'italic' });
|
|
201
|
+
|
|
202
|
+
trigger.registry = registry;
|
|
203
|
+
container.registry = registry;
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
```html
|
|
207
|
+
<!-- label and icon resolved from registry -->
|
|
208
|
+
<ixfx-menu-item command="bold"></ixfx-menu-item>
|
|
209
|
+
<ixfx-menu-item command="italic"></ixfx-item>
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Keyboard Shortcuts
|
|
213
|
+
|
|
214
|
+
Pass a `KeyboardManager` to display shortcuts next to menu items:
|
|
215
|
+
|
|
216
|
+
```ts
|
|
217
|
+
import { KeyboardManager } from '@clinth/ui-commands';
|
|
218
|
+
|
|
219
|
+
keyboardManager.bind('bold', { key: 'b', modifiers: ['ctrl'] });
|
|
220
|
+
keyboardManager.bind('italic', { key: 'i', modifiers: ['ctrl'] });
|
|
221
|
+
|
|
222
|
+
trigger.keyboardManager = keyboardManager;
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Styling
|
|
226
|
+
|
|
227
|
+
### CSS Custom Properties
|
|
228
|
+
|
|
229
|
+
| Variable | Default | Description |
|
|
230
|
+
|----------|---------|-------------|
|
|
231
|
+
| `--_item-hover-bg` | `var(--surface-6)` | Background on mouse hover |
|
|
232
|
+
| `--_item-focused-bg` | `var(--surface-6)` | Background when keyboard-focused (tickled) |
|
|
233
|
+
| `--_trigger-hover-bg` | `var(--surface-4)` | Trigger button hover background |
|
|
234
|
+
| `--_trigger-hover-opacity` | `1` | Trigger button hover opacity |
|
|
235
|
+
|
|
236
|
+
The popup surface uses these from `popupSurfaceStyles`:
|
|
237
|
+
|
|
238
|
+
| Variable | Description |
|
|
239
|
+
|----------|-------------|
|
|
240
|
+
| `--surface-5` | Menu background |
|
|
241
|
+
| `--surface-6` | Menu border |
|
|
242
|
+
| `--surface-5-text` | Menu text color |
|
|
243
|
+
| `--shadow-m` | Box shadow |
|
|
244
|
+
|
|
245
|
+
### Custom Item Background Colors
|
|
246
|
+
|
|
247
|
+
```css
|
|
248
|
+
/* Apply to ixfx-menu-container */
|
|
249
|
+
ixfx-menu-container {
|
|
250
|
+
--_item-hover-bg: #ffdddd;
|
|
251
|
+
--_item-focused-bg: #ffcccc;
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Custom Trigger Styling
|
|
256
|
+
|
|
257
|
+
```css
|
|
258
|
+
ixfx-menu-trigger {
|
|
259
|
+
--_trigger-hover-bg: #eeeeee;
|
|
260
|
+
--_trigger-hover-opacity: 0.8;
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Per-Item Styling
|
|
265
|
+
|
|
266
|
+
Style menu items from the outside by targeting their tag name or by attaching a class attribute and selecting within:
|
|
267
|
+
|
|
268
|
+
```css
|
|
269
|
+
ixfx-menu-item[command="delete"] .item {
|
|
270
|
+
color: red;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/* Target a specific menu via a wrapper */
|
|
274
|
+
.file-menu ixfx-menu-item .item {
|
|
275
|
+
font-weight: bold;
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## Events
|
|
280
|
+
|
|
281
|
+
| Event | Detail | Description |
|
|
282
|
+
|-------|--------|-------------|
|
|
283
|
+
| `command` | `{ command, args, checked? }` | Fired by `ixfx-menu-item`, `ixfx-menu-check-item` |
|
|
284
|
+
| `radio-select` | `{ value }` | Fired by `ixfx-menu-radio-item` |
|
|
285
|
+
| `radio-change` | `{ name, value }` | Fired by `ixfx-menu-radio-group` when selection changes |
|
|
286
|
+
| `menu-command` | `{ command, value?, checked? }` | Bubbles up from `ixfx-menu-trigger` |
|
|
287
|
+
|
|
288
|
+
## `ixfx-menu-command-controller`
|
|
289
|
+
|
|
290
|
+
A helper class that listens for `command` events and invokes a `CommandRegistry`:
|
|
291
|
+
|
|
292
|
+
```ts
|
|
293
|
+
import { MenuCommandController } from './menu/menu-command-controller.js';
|
|
294
|
+
|
|
295
|
+
const ctrl = new MenuCommandController({ registry, target: document });
|
|
296
|
+
ctrl.start();
|
|
297
|
+
|
|
298
|
+
// Now any menu item with a matching command will invoke the registry
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## Mode Reference
|
|
302
|
+
|
|
303
|
+
`ixfx-menu-trigger` supports three `mode` values:
|
|
304
|
+
|
|
305
|
+
| Mode | Trigger Behavior |
|
|
306
|
+
|------|-----------------|
|
|
307
|
+
| `trigger` | Shows a button labeled with the `label` property |
|
|
308
|
+
| `dropdown` | No button shown; expects a custom trigger in the `trigger` slot |
|
|
309
|
+
| `context` | Opens at cursor position on right-click |
|
|
310
|
+
|
|
311
|
+
## Placement
|
|
312
|
+
|
|
313
|
+
The `placement` attribute controls popover anchoring:
|
|
314
|
+
|
|
315
|
+
| Value | Position |
|
|
316
|
+
|-------|----------|
|
|
317
|
+
| `bottom-start` | Below trigger, left edge aligned (default) |
|
|
318
|
+
| `bottom-end` | Below trigger, right edge aligned |
|
|
319
|
+
| `right-start` | To the right of trigger, top edge aligned |
|