Skip to content

Commit 686b377

Browse files
committed
Start implementing icons for menu items.
1 parent 33f5492 commit 686b377

File tree

7 files changed

+276
-3
lines changed

7 files changed

+276
-3
lines changed

src/pg/menuItem/README.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
# `<pg-menu>`
22

3-
The `pg-menu-item` is usually rendered as a child of the `pg-menu` as a `type: PgMenuItem`.
3+
The `PgMenuItem` is used with the `pg-menu` as a `type: PgMenuItem`.
4+
5+
For icon support use `PgMenuItemIcon`.
46

57
## Usage
68

79
```typescript
810
import '@pictogrammers/components/pgMenuItem.js';
911
```
1012

11-
```html
12-
<pg-menu-item></pg-menu-item>
13+
```typescript
14+
this.$items = [{
15+
type: PgMenuItem,
16+
label: 'Item 1'
17+
}];
1318
```
1419

1520
| Attributes | Tested | Description |

src/pg/menuItemIcon/README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# `<pg-menu>`
2+
3+
The `PgMenuItemIcon` is the `PgMenuItem` with icon support. The `pg-menu` can use both in the same list, but this component has the overhead of includig `PgIcon`.
4+
5+
## Usage
6+
7+
```typescript
8+
import '@pictogrammers/components/pgMenuItemIcon.js';
9+
```
10+
11+
```html
12+
<pg-menu-item></pg-menu-item>
13+
```
14+
15+
| Attributes | Tested | Description |
16+
| ---------- | -------- | ----------- |
17+
| `label` | | Item label. |
18+
| `icon` | | Item icon. |
19+
| `checked` | | Item checked. |
20+
| `disabled` | | Item disabled. |
21+
22+
## Events
23+
24+
```typescript
25+
this.$item.addEventListener('select', (e: any) => {
26+
const { index } = e.detail;
27+
});
28+
```
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<div class="example">
2+
<pg-menu-item-icon part="item"></pg-menu-item-icon>
3+
</div>
4+
<div>
5+
<table style="border: 1px solid #ddd;margin-block-start: 0.5rem;">
6+
<thead>
7+
<tr>
8+
<th>Property</th>
9+
<th>Value</th>
10+
<th>&nbsp;</th>
11+
</tr>
12+
</thead>
13+
<tbody>
14+
<tr>
15+
<td><code>icon</code></td>
16+
<td part="iconValue"></td>
17+
<td>
18+
<button part="iconFile">File</button>
19+
<button part="iconFolder">Folder</button>
20+
</td>
21+
</tr>
22+
<tr>
23+
<td><code>checked</code></td>
24+
<td part="checkedValue"></td>
25+
<td><input part="checkedToggle" type="checkbox"/></td>
26+
</tr>
27+
<tr>
28+
<td><code>disabled</code></td>
29+
<td part="disabledValue"></td>
30+
<td><input part="disabledToggle" type="checkbox"/></td>
31+
</tr>
32+
</tbody>
33+
</table>
34+
</div>
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { Component, Part } from '@pictogrammers/element';
2+
3+
import PgMenuItem from '../../menuItemIcon';
4+
5+
import template from './basic.html';
6+
7+
const IconFile = 'M13,9V3.5L18.5,9M6,2C4.89,2 4,2.89 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2H6Z';
8+
const IconFolder = 'M10,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V8C22,6.89 21.1,6 20,6H12L10,4Z';
9+
10+
@Component({
11+
selector: 'x-pg-menu-item-icon-basic',
12+
template
13+
})
14+
export default class XPgMenuItemIconBasic extends HTMLElement {
15+
@Part() $item: PgMenuItem;
16+
@Part() $checkedToggle: HTMLInputElement;
17+
@Part() $disabledToggle: HTMLInputElement;
18+
@Part() $iconFile: HTMLButtonElement;
19+
@Part() $iconFolder: HTMLButtonElement;
20+
@Part() $checkedValue: HTMLDivElement;
21+
@Part() $disabledValue: HTMLDivElement;
22+
23+
connectedCallback() {
24+
this.$item.icon = IconFile;
25+
this.$item.label = 'Item 1';
26+
this.$item.checked = false;
27+
this.$item.disabled = false;
28+
29+
this.$checkedValue.textContent = `${this.$item.checked}`;
30+
this.$disabledValue.textContent = `${this.$item.disabled}`;
31+
32+
this.$item.addEventListener('select', (e: any) => {
33+
this.$checkedToggle.checked = e.target.checked;
34+
this.$checkedValue.textContent = `${this.$item.checked}`;
35+
});
36+
37+
this.$iconFile.addEventListener('click', () => {
38+
this.$item.icon = IconFile;
39+
});
40+
41+
this.$iconFolder.addEventListener('click', () => {
42+
this.$item.icon = IconFolder;
43+
});
44+
45+
this.$checkedToggle.addEventListener('change', (e: any) => {
46+
this.$item.checked = e.target.checked;
47+
this.$checkedValue.textContent = `${this.$item.checked}`;
48+
});
49+
50+
this.$disabledToggle.addEventListener('change', (e: any) => {
51+
this.$item.disabled = e.target.checked;
52+
this.$disabledValue.textContent = `${this.$item.disabled}`;
53+
});
54+
}
55+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
:host {
2+
display: contents;
3+
}
4+
5+
[part=button] {
6+
outline: none;
7+
display: flex;
8+
align-items: center;
9+
font-family: var(--pg-font-family);
10+
text-align: var(--pg-menu-item-text-align, left);
11+
background: var(--pg-menu-item-background, transparent);
12+
padding: var(--pg-menu-item-padding, 0.25rem 0.5rem 0.25rem 1.5rem);
13+
border-color: transparent;
14+
border-width: 0;
15+
border-style: solid;
16+
border-top-left-radius: var(--pg-menu-item-border-radius-top, 0.25rem);
17+
border-top-right-radius: var(--pg-menu-item-border-radius-top, 0.25rem);
18+
border-bottom-left-radius: var(--pg-menu-item-border-radius-bottom, 0.25rem);
19+
border-bottom-right-radius: var(--pg-menu-item-border-radius-bottom, 0.25rem);
20+
color: var(--pg-menu-item-color, #453C4F);
21+
}
22+
23+
[part=button]:not(:disabled):active,
24+
[part=button]:not(:disabled):hover {
25+
background: var(--pg-menu-item-selected-background, #453C4F);
26+
color: #FFFFFF;
27+
}
28+
29+
[part=button]:not(:disabled):active {
30+
background: var(--pg-menu-item-active-background, #5f516e);
31+
}
32+
33+
[part=button]:disabled {
34+
color: var(--pg-menu-item-disabled-color, rgb(69, 60, 79, 0.75));
35+
}
36+
37+
[part=button].checked::before {
38+
position: absolute;
39+
translate: -1.5rem 0;
40+
content: var(--pg-menu-item-check, url("data:image/svg+xml; utf8, <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M 17.5,10.2501L 10.5,17.25L 6.5,13.25L 7.9,11.8L 10.5,14.4L 16.0857,8.8L 17.5,10.25Z' fill='rgb(69, 60, 79)' /></svg>"));
41+
width: 1.5rem;
42+
height: 1.5rem;
43+
}
44+
45+
[part=button].checked:active::before,
46+
[part=button].checked:hover::before {
47+
content: var(--pg-menu-item-hover-check, url("data:image/svg+xml; utf8, <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M 17.5,10.2501L 10.5,17.25L 6.5,13.25L 7.9,11.8L 10.5,14.4L 16.0857,8.8L 17.5,10.25Z' fill='white' /></svg>"));
48+
}
49+
50+
[part=button].checked:disabled::before {
51+
content: var(--pg-menu-item-disabled-check, url("data:image/svg+xml; utf8, <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M 17.5,10.2501L 10.5,17.25L 6.5,13.25L 7.9,11.8L 10.5,14.4L 16.0857,8.8L 17.5,10.25Z' fill='rgb(69, 60, 79, 0.5)' /></svg>"));
52+
}
53+
54+
[part=button]:focus-visible {
55+
position: relative;
56+
}
57+
58+
[part=button]:focus-visible::after {
59+
pointer-events: none;
60+
content: '';
61+
position: absolute;
62+
top: -1px;
63+
right: -1px;
64+
bottom: -1px;
65+
left: -1px;
66+
border-radius: 0.25rem;
67+
box-shadow: 0 0 0 3px var(--pg-focus-color, rgb(79, 143, 249, 0.5));
68+
}
69+
70+
[part=button]:focus-visible:not(:hover)::after {
71+
background: var(--pg-focus-background-color, rgb(79, 143, 249, 0.1));
72+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<button part="button">
2+
<pg-icon part="icon"></pg-icon>
3+
<span part="label"></span>
4+
</button>
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { Component, Prop, Part } from '@pictogrammers/element';
2+
3+
import PgIcon from '../icon/icon';
4+
5+
import template from './menuItemIcon.html';
6+
import style from './menuItemIcon.css';
7+
8+
const noIcon = 'M0 0h24v24H0V0zm2 2v20h20V2H2z';
9+
10+
@Component({
11+
selector: 'pg-menu-item-icon',
12+
style,
13+
template
14+
})
15+
export default class PgMenuItemIcon extends HTMLElement {
16+
static delegatesFocus = true;
17+
18+
@Prop() index: number;
19+
@Prop() icon: string = '';
20+
@Prop() label: string = '';
21+
@Prop() checked: boolean = false;
22+
@Prop() disabled: boolean = false;
23+
24+
@Part() $icon: PgIcon;
25+
@Part() $label: HTMLButtonElement;
26+
27+
render(changes) {
28+
if (changes.icon) {
29+
this.$icon.path = this.icon;
30+
}
31+
if (changes.label) {
32+
this.$label.textContent = this.label;
33+
}
34+
if (changes.disabled) {
35+
this.$label.disabled = this.disabled;
36+
}
37+
if (changes.checked) {
38+
this.$label.classList.toggle('checked', this.checked);
39+
}
40+
}
41+
42+
connectedCallback() {
43+
this.$label.addEventListener('click', () => {
44+
this.checked = true;
45+
this.dispatchEvent(new CustomEvent('select', {
46+
detail: { index: this.index }
47+
}));
48+
});
49+
this.$label.addEventListener('keydown', (e: KeyboardEvent) => {
50+
switch (e.key) {
51+
case 'ArrowDown':
52+
this.dispatchEvent(new CustomEvent('down', {
53+
detail: { index: this.index }
54+
}));
55+
e.preventDefault();
56+
break;
57+
case 'ArrowUp':
58+
this.dispatchEvent(new CustomEvent('up', {
59+
detail: { index: this.index }
60+
}));
61+
e.preventDefault();
62+
break;
63+
}
64+
});
65+
}
66+
67+
focus() {
68+
this.$label.focus();
69+
}
70+
71+
getHeight(): number {
72+
return this.$label.getBoundingClientRect().height;
73+
}
74+
75+
}

0 commit comments

Comments
 (0)