Skip to content

Commit 82de7d0

Browse files
committed
Breakout menu types.
1 parent 228d616 commit 82de7d0

File tree

16 files changed

+453
-3
lines changed

16 files changed

+453
-3
lines changed

src/pg/inputSelect/inputSelect.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Component, Prop, Part, forEach } from '@pictogrammers/element';
2-
import PgOverlayMenu from '../overlayMenu/overlayMenu';
2+
import PgOverlaySelectMenu from '../overlaySelectMenu/overlaySelectMenu';
33

44
import template from './inputSelect.html';
55
import style from './inputSelect.css';
@@ -41,7 +41,7 @@ export default class PgInputSelect extends HTMLElement {
4141
async #handleClick() {
4242
if (this.#menuOpen) { return; }
4343
this.#menuOpen = true;
44-
const result = await PgOverlayMenu.open({
44+
const result = await PgOverlaySelectMenu.open({
4545
source: this.$button,
4646
default: this.default,
4747
value: this.options.find(x => x.value === this.value) ?? null,
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Overlay Menu
2+
3+
The `PgOverlayContextMenu` creates an overlay menu above a source element. For standard menu lists use the `PgMenuItem` for the check.
4+
5+
Components that use `PgOverlayContextMenu` include:
6+
7+
- `pg-input-select`
8+
9+
## Usage
10+
11+
```typescript
12+
import PgMenuItem from '@pictogrammers/components/pg/menuItem';
13+
14+
#isOpen: false;
15+
handleSourceClick() {
16+
if (this.#isOpen) { return; }
17+
this.#isOpen = true;
18+
const result = await PgOverlayContextMenu.open({
19+
source: this.$element,
20+
items: [{
21+
label: 'Item 1',
22+
value: 'item1',
23+
type: PgMenuItem
24+
}, {
25+
label: 'Item 2',
26+
value: 'item2',
27+
type: PgMenuItem
28+
}]
29+
});
30+
this.#isOpen = false;
31+
console.log(result);
32+
}
33+
```
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
p {
2+
margin: 0.5rem 0;
3+
}
4+
5+
[part=area] {
6+
display: flex;
7+
width: 20rem;
8+
height: 6rem;
9+
border: 1px dashed rgb(79, 143, 249);
10+
border-radius: 1rem;
11+
align-items: center;
12+
justify-content: center;
13+
background: var(--pg-focus-background-color, rgba(79, 143, 249, 0.1));
14+
color: rgba(79, 143, 249, 0.75);
15+
text-shadow: 0 0 4px #fff;
16+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<div class="example">
2+
<div part="area">
3+
Right Click Here
4+
</div>
5+
<p>Result: <code part="result"></code></p></p>
6+
<p>input: <code part="input"></code></p></p>
7+
</div>
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { Component, Part, Prop } from '@pictogrammers/element';
2+
3+
import PgMenuItem from '../../../menuItem/menuItem';
4+
import PgMenuDivider from '../../../menuDivider/menuDivider';
5+
import PgOverlayContextMenu from '../../overlayContextMenu';
6+
7+
import template from './basic.html';
8+
import style from './basic.css';
9+
10+
@Component({
11+
selector: 'x-pg-overlay-context-menu-basic',
12+
template,
13+
style
14+
})
15+
export default class XPgOverlayContextMenuBasic extends HTMLElement {
16+
@Part() $area: HTMLDivElement;
17+
@Part() $result: HTMLSpanElement;
18+
@Part() $input: HTMLSpanElement;
19+
20+
connectedCallback() {
21+
this.$area.addEventListener('contextmenu', this.#handleContextMenu.bind(this));
22+
}
23+
24+
#value = null;
25+
26+
#menuOpen = false;
27+
async #handleContextMenu(e: any) {
28+
if (this.#menuOpen) { return; }
29+
e.preventDefault();
30+
const items = [{
31+
label: 'Item 1',
32+
value: 'item1',
33+
type: PgMenuItem
34+
},
35+
{
36+
label: 'Item 2',
37+
value: 'item2',
38+
type: PgMenuItem
39+
},
40+
{
41+
type: PgMenuDivider
42+
},
43+
{
44+
label: 'Item 3',
45+
value: 'item3',
46+
type: PgMenuItem
47+
}];
48+
this.#menuOpen = true;
49+
const result = await PgOverlayContextMenu.open({
50+
source: this.$area,
51+
value: this.#value,
52+
items,
53+
oninput: (value) => {
54+
this.$input.textContent = value;
55+
}
56+
});
57+
if (result !== undefined) {
58+
this.#value = result;
59+
}
60+
this.$result.textContent = result;
61+
this.#menuOpen = false;
62+
}
63+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
:host {
2+
display: contents;
3+
}
4+
5+
[part="overlay"] {
6+
margin: 0;
7+
padding: 0;
8+
border: 0;
9+
background: transparent;
10+
overflow: visible;
11+
--pg-menu-box-shadow: 0 0.125rem 0.5rem rgba(0, 0, 0, 0.25);
12+
top: anchor(top);
13+
left: anchor(left);
14+
min-width: calc(anchor-size(width) + calc(2 * var(--pg-menu-padding, 0.25rem)));
15+
translate: var(--pg-overlay-menu-_x, 0) var(--pg-overlay-menu-_y, 0);
16+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<div part="overlay">
2+
<pg-menu part="menu"></pg-menu>
3+
</div>
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { Component, Part, Prop } from '@pictogrammers/element';
2+
3+
import PgMenu from '../menu/menu';
4+
import PgOverlay from '../overlay/overlay';
5+
6+
import template from './overlayContextMenu.html';
7+
import style from './overlayContextMenu.css';
8+
9+
@Component({
10+
selector: 'pg-overlay-context-menu',
11+
template,
12+
style
13+
})
14+
export default class PgOverlayContextMenu extends PgOverlay {
15+
@Part() $overlay: HTMLDivElement;
16+
@Part() $menu: PgMenu;
17+
18+
@Prop() source: HTMLElement | null = null;
19+
@Prop() default: any = null;
20+
@Prop() items: any[] = [];
21+
@Prop() value: any = null;
22+
23+
render(changes) {
24+
if (changes.items) {
25+
if (this.value !== null) {
26+
this.items.forEach(item => item.checked = false);
27+
this.items.find(item => item.value === this.value.value).checked = true;
28+
}
29+
this.$menu.items = this.items;
30+
}
31+
}
32+
33+
connectedCallback() {
34+
this.$menu.addEventListener('select', this.#handleSelect.bind(this));
35+
this.$overlay.popover = 'auto';
36+
if (this.source !== null) {
37+
// @ts-ignore
38+
this.$overlay.showPopover({
39+
source: this.source
40+
});
41+
}
42+
this.$overlay.addEventListener('toggle', this.#toggle.bind(this));
43+
// Position
44+
const rect = this.source?.getBoundingClientRect();
45+
let x = 0, y = 0;
46+
const value = this.value === null || typeof this.value !== 'object'
47+
? this.value
48+
: this.value.value;
49+
// value is an item in the items list
50+
const index = this.value === null
51+
? -1
52+
: this.items.findIndex(x => x.value === value);
53+
if (index !== -1) {
54+
const height = this.$menu.getItemHeight(index);
55+
// Overlap item
56+
y -= this.$menu.getItemOffset(0, index);
57+
if (rect?.height !== height && rect?.height) {
58+
y += (rect.height - height) / 2;
59+
}
60+
} else if (this.items.length > 0) {
61+
// insert default if defined
62+
if (this.default) {
63+
this.default.checked = true;
64+
this.$menu.items.unshift(this.default);
65+
}
66+
// focus first item
67+
const height = this.$menu.getItemHeight(0);
68+
y -= this.$menu.getItemOffset(0, 0);
69+
if (rect?.height !== height && rect?.height) {
70+
y += (rect.height - height) / 2;
71+
}
72+
}
73+
// ToDo: update to CSS Variables
74+
this.$overlay.style.setProperty('--pg-overlay-menu-_x', `${x}px`);
75+
this.$overlay.style.setProperty('--pg-overlay-menu-_y', `${y}px`);
76+
// Focus
77+
this.$menu.focus(index);
78+
}
79+
80+
#toggle(e: ToggleEvent) {
81+
if (e.newState === 'closed') {
82+
this.close();
83+
this.source?.focus();
84+
}
85+
}
86+
87+
disconnectedCallback() {
88+
}
89+
90+
#handleSelect(e: any) {
91+
e.detail.item.index = e.detail.index;
92+
this.close(e.detail.item);
93+
this.source?.focus();
94+
}
95+
}

src/pg/overlayMenu/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ The `PgOverlayMenu` creates an overlay menu above a source element. For standard
44

55
Components that use `PgOverlayMenu` include:
66

7-
- `pg-input-select`
7+
- `pg-button-menu`
88

99
## Usage
1010

src/pg/overlaySelectMenu/README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Overlay Menu
2+
3+
The `PgOverlaySelectMenu` creates an overlay menu above a source element. For standard menu lists use the `PgMenuItem` for the check.
4+
5+
Components that use `PgOverlaySelectMenu` include:
6+
7+
- `pg-input-select`
8+
9+
## Usage
10+
11+
```typescript
12+
import PgMenuItem from '@pictogrammers/components/pg/menuItem';
13+
14+
#isOpen: false;
15+
handleSourceClick() {
16+
if (this.#isOpen) { return; }
17+
this.#isOpen = true;
18+
const result = await PgOverlayMenu.open({
19+
source: this.$element,
20+
items: [{
21+
label: 'Item 1',
22+
value: 'item1',
23+
type: PgMenuItem
24+
}, {
25+
label: 'Item 2',
26+
value: 'item2',
27+
type: PgMenuItem
28+
}]
29+
});
30+
this.#isOpen = false;
31+
console.log(result);
32+
}
33+
```

0 commit comments

Comments
 (0)