@pictogrammers/components 0.5.18 → 0.5.20
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/package.json +1 -1
- package/pg/button/button.css +5 -12
- package/pg/header/README.md +1 -1
- package/pg/header/__examples__/basic/basic.html +1 -1
- package/pg/inputNumber/inputNumber.css +11 -5
- package/pg/modification/__examples__/basic/basic.html +1 -1
- package/pg/tree/README.md +122 -81
- package/pg/tree/tree.ts +17 -19
- package/pg/treeButtonIcon/treeButtonIcon.css +6 -1
- package/pg/treeButtonIcon/treeButtonIcon.ts +52 -42
package/package.json
CHANGED
package/pg/button/button.css
CHANGED
|
@@ -36,25 +36,18 @@
|
|
|
36
36
|
--pg-icon-color: var(--pg-button-hover-color, #FFFFFF);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
[part=button]:hover:active {
|
|
40
|
+
box-shadow: 0 1px 0.25rem rgba(0, 0, 0, 1) inset;
|
|
41
|
+
}
|
|
42
|
+
|
|
39
43
|
[part=button]:active {
|
|
40
|
-
box-shadow: 0 1px 0.25rem rgba(0, 0, 0, 0.
|
|
41
|
-
position: relative;
|
|
44
|
+
box-shadow: 0 1px 0.25rem rgba(0, 0, 0, 0.3) inset;
|
|
42
45
|
}
|
|
43
46
|
|
|
44
47
|
[part=button]:focus-visible {
|
|
45
48
|
position: relative;
|
|
46
49
|
}
|
|
47
50
|
|
|
48
|
-
[part=button]:active::before {
|
|
49
|
-
content: '';
|
|
50
|
-
position: absolute;
|
|
51
|
-
top: -1px;
|
|
52
|
-
right: -1px;
|
|
53
|
-
bottom: -1px;
|
|
54
|
-
left: -1px;
|
|
55
|
-
border-radius: var(--pg-button-border-radius, 0.25rem);
|
|
56
|
-
box-shadow: 0 0 0 3px var(--pg-focus-color, rgb(79, 143, 249, 0.6));
|
|
57
|
-
}
|
|
58
51
|
[part=button]:focus-visible::before {
|
|
59
52
|
pointer-events: none;
|
|
60
53
|
content: '';
|
package/pg/header/README.md
CHANGED
|
@@ -14,11 +14,17 @@
|
|
|
14
14
|
font-size: 1rem;
|
|
15
15
|
outline: none;
|
|
16
16
|
field-sizing: content;
|
|
17
|
-
min-width:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
min-width: max(
|
|
18
|
+
1rem,
|
|
19
|
+
var(
|
|
20
|
+
--pg-input-number-min-width,
|
|
21
|
+
calc(
|
|
22
|
+
100%
|
|
23
|
+
- var(--pg-input-number-padding-inline-start, var(--pg-input-number-padding-inline, 0.5rem))
|
|
24
|
+
- var(--pg-input-number-padding-inline-end, var(--pg-input-number-padding-inline, 0.5rem))
|
|
25
|
+
- 2px
|
|
26
|
+
)
|
|
27
|
+
)
|
|
22
28
|
);
|
|
23
29
|
max-width: calc(
|
|
24
30
|
var(--pg-input-number-max-width, 100%)
|
package/pg/tree/README.md
CHANGED
|
@@ -1,81 +1,122 @@
|
|
|
1
|
-
# Tree
|
|
2
|
-
|
|
3
|
-
The `pg-tree` is used to render a tree list of items.
|
|
4
|
-
|
|
5
|
-
- Folder / Item symbols
|
|
6
|
-
- Drag and Drop
|
|
7
|
-
- Context Menu
|
|
8
|
-
|
|
9
|
-
## Usage
|
|
10
|
-
|
|
11
|
-
While setup for a normal file tree this can be used for any folder structure.
|
|
12
|
-
|
|
13
|
-
```typescript
|
|
14
|
-
import PgTree, { SelectedTreeItem } from '@pictogrammers/components/pg/tree';
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
##
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
this
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
1
|
+
# Tree
|
|
2
|
+
|
|
3
|
+
The `pg-tree` is used to render a tree list of items.
|
|
4
|
+
|
|
5
|
+
- Folder / Item symbols
|
|
6
|
+
- Drag and Drop
|
|
7
|
+
- Context Menu
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
While setup for a normal file tree this can be used for any folder structure.
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import PgTree, { SelectedTreeItem } from '@pictogrammers/components/pg/tree';
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Item Data Shape
|
|
18
|
+
|
|
19
|
+
Each item in the `items` array follows this structure:
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
{
|
|
23
|
+
label: string;
|
|
24
|
+
icon: { path: string };
|
|
25
|
+
isFolder: boolean; // true enables the drop-onto zone and .items CSS class
|
|
26
|
+
expanded?: boolean; // folders only
|
|
27
|
+
items?: any[]; // child items (folders only)
|
|
28
|
+
actions?: {
|
|
29
|
+
type: typeof PgTreeButtonIcon;
|
|
30
|
+
icon: string;
|
|
31
|
+
enabled: boolean;
|
|
32
|
+
label?: string; // used as button title / aria-label
|
|
33
|
+
}[];
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Events
|
|
38
|
+
|
|
39
|
+
- `select` — one or more items selected / deselected
|
|
40
|
+
- `rename` — item label edited inline
|
|
41
|
+
- `menu` — right-click context menu requested
|
|
42
|
+
- `action` — an action button clicked
|
|
43
|
+
- `move` — drag-and-drop reorder completed
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
this.$tree.addEventListener('select', (e: any) => {
|
|
47
|
+
const { items } = e.detail;
|
|
48
|
+
this.selectedItems = items;
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
this.$tree.addEventListener('rename', (e: any) => {
|
|
54
|
+
const { item, label } = e.detail;
|
|
55
|
+
item.getData().label = label;
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
this.$tree.addEventListener('menu', (e: any) => {
|
|
61
|
+
const { item, x, y } = e.detail;
|
|
62
|
+
// show context menu at (x, y) for item
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
this.$tree.addEventListener('action', (e: any) => {
|
|
68
|
+
const { item, actionIndex } = e.detail;
|
|
69
|
+
switch (actionIndex) {
|
|
70
|
+
case 0:
|
|
71
|
+
// toggle visibility
|
|
72
|
+
break;
|
|
73
|
+
case 1:
|
|
74
|
+
item.remove();
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
this.$tree.addEventListener('move', (e: any) => {
|
|
82
|
+
const { item, position } = e.detail;
|
|
83
|
+
// position is 'before' | 'after' | 'on'
|
|
84
|
+
this.selectedItems.forEach((selected: SelectedTreeItem) => {
|
|
85
|
+
selected.move(item, position);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## SelectedTreeItem API
|
|
91
|
+
|
|
92
|
+
The `item` object passed in event details wraps the raw data with helper methods:
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
item.indexes // number[] — path to this item in the tree
|
|
96
|
+
item.getData() // returns the raw data object
|
|
97
|
+
item.getParentData() // returns the parent's raw data (or the tree root)
|
|
98
|
+
item.remove() // removes the item and clears selection
|
|
99
|
+
item.move(targetItem, position) // moves to 'before' | 'after' | 'on' the target
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Custom Size
|
|
103
|
+
|
|
104
|
+
Resize the row height via a CSS variable (default `2`, range `1`–`2`):
|
|
105
|
+
|
|
106
|
+
```css
|
|
107
|
+
pg-tree {
|
|
108
|
+
--pg-tree-size: 1.5;
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## CSS Custom Properties
|
|
113
|
+
|
|
114
|
+
| Property | Description |
|
|
115
|
+
|---|---|
|
|
116
|
+
| `--pg-tree-background` | Background of the tree container |
|
|
117
|
+
| `--pg-tree-item-background` | Default item row background |
|
|
118
|
+
| `--pg-tree-item-color` | Icon and text color |
|
|
119
|
+
| `--pg-tree-item-font-size` | Label font size |
|
|
120
|
+
| `--pg-tree-selected-primary` | Selection indicator / toggle color |
|
|
121
|
+
| `--pg-tree-selected-secondary` | Selected row background |
|
|
122
|
+
| `--pg-tree-selected-secondary-hover` | Selected row hover background |
|
package/pg/tree/tree.ts
CHANGED
|
@@ -49,11 +49,7 @@ export default class PgTree extends HTMLElement {
|
|
|
49
49
|
this.dispatchEvent(new CustomEvent('move', {
|
|
50
50
|
detail: { position: e.detail.position }
|
|
51
51
|
}));
|
|
52
|
-
this
|
|
53
|
-
detail: {
|
|
54
|
-
items: [...this.#selectedIndexes.values()].map((idxs: any) => this.#wrap(idxs)),
|
|
55
|
-
}
|
|
56
|
-
}));
|
|
52
|
+
this.#dispatchSelect();
|
|
57
53
|
});
|
|
58
54
|
this.$items.addEventListener('rename', (e: any) => {
|
|
59
55
|
e.stopPropagation();
|
|
@@ -114,11 +110,7 @@ export default class PgTree extends HTMLElement {
|
|
|
114
110
|
}
|
|
115
111
|
}
|
|
116
112
|
|
|
117
|
-
this
|
|
118
|
-
detail: {
|
|
119
|
-
items: [...this.#selectedIndexes.values()].map((idxs: any) => this.#wrap(idxs)),
|
|
120
|
-
}
|
|
121
|
-
}));
|
|
113
|
+
this.#dispatchSelect();
|
|
122
114
|
});
|
|
123
115
|
this.$items.addEventListener('up', (e: any) => {
|
|
124
116
|
e.stopPropagation();
|
|
@@ -141,9 +133,7 @@ export default class PgTree extends HTMLElement {
|
|
|
141
133
|
this.#selectedIndexes.forEach((idxs: any) => this.#getItem(idxs).selected = false);
|
|
142
134
|
this.#selectedIndexes.clear();
|
|
143
135
|
this.#lastSelectedIndexes = null;
|
|
144
|
-
this
|
|
145
|
-
detail: { items: [] }
|
|
146
|
-
}));
|
|
136
|
+
this.#dispatchSelect();
|
|
147
137
|
} else if (e.key === 'Delete') {
|
|
148
138
|
const toRemove = [...this.#selectedIndexes.values()] as number[][];
|
|
149
139
|
// Remove higher sibling indexes first to avoid index shifts
|
|
@@ -158,6 +148,7 @@ export default class PgTree extends HTMLElement {
|
|
|
158
148
|
toRemove.forEach((indexes: number[]) => this.#removeItem(indexes));
|
|
159
149
|
this.#selectedIndexes.clear();
|
|
160
150
|
this.#lastSelectedIndexes = null;
|
|
151
|
+
this.#dispatchSelect();
|
|
161
152
|
}
|
|
162
153
|
});
|
|
163
154
|
this.$items.addEventListener('itemdragstart', (e: any) => {
|
|
@@ -186,12 +177,7 @@ export default class PgTree extends HTMLElement {
|
|
|
186
177
|
}
|
|
187
178
|
});
|
|
188
179
|
this.$items.classList.toggle('dragging', true);
|
|
189
|
-
|
|
190
|
-
this.dispatchEvent(new CustomEvent('select', {
|
|
191
|
-
detail: {
|
|
192
|
-
items: [...this.#selectedIndexes.values()].map((idxs: any) => this.#wrap(idxs)),
|
|
193
|
-
}
|
|
194
|
-
}));
|
|
180
|
+
this.#dispatchSelect();
|
|
195
181
|
});
|
|
196
182
|
this.$items.addEventListener('itemdragend', () => {
|
|
197
183
|
this.#draggingElements.forEach(el => el.classList.remove('dragging'));
|
|
@@ -351,6 +337,7 @@ export default class PgTree extends HTMLElement {
|
|
|
351
337
|
this.#removeItem(indexes);
|
|
352
338
|
this.#selectedIndexes.clear();
|
|
353
339
|
this.#lastSelectedIndexes = null;
|
|
340
|
+
this.#dispatchSelect();
|
|
354
341
|
},
|
|
355
342
|
getData: () => this.#getItem(indexes),
|
|
356
343
|
getParentData: () => {
|
|
@@ -359,6 +346,7 @@ export default class PgTree extends HTMLElement {
|
|
|
359
346
|
},
|
|
360
347
|
move: (item: SelectedTreeItem, position: string) => {
|
|
361
348
|
this.#batchMove([indexes], item.indexes, position);
|
|
349
|
+
this.#dispatchSelect();
|
|
362
350
|
}
|
|
363
351
|
};
|
|
364
352
|
}
|
|
@@ -368,11 +356,21 @@ export default class PgTree extends HTMLElement {
|
|
|
368
356
|
const unproxyItem = getProxyValue(item);
|
|
369
357
|
item.selected = false;
|
|
370
358
|
this.#selectedIndexes.delete(unproxyItem);
|
|
359
|
+
this.#dispatchSelect();
|
|
371
360
|
}
|
|
372
361
|
|
|
373
362
|
unselectAll() {
|
|
374
363
|
this.#selectedIndexes.forEach((idxs: any) => this.#getItem(idxs).selected = false);
|
|
375
364
|
this.#selectedIndexes.clear();
|
|
365
|
+
this.#dispatchSelect();
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
#dispatchSelect() {
|
|
369
|
+
this.dispatchEvent(new CustomEvent('select', {
|
|
370
|
+
detail: {
|
|
371
|
+
items: [...this.#selectedIndexes.values()].map((idxs: any) => this.#wrap(idxs)),
|
|
372
|
+
}
|
|
373
|
+
}));
|
|
376
374
|
}
|
|
377
375
|
|
|
378
376
|
#calculateDragExcludes(): string[] {
|
|
@@ -11,8 +11,13 @@
|
|
|
11
11
|
border-radius: 0.125rem;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
[part="button"]:hover {
|
|
14
|
+
[part="button"]:hover:not(:disabled) {
|
|
15
15
|
box-shadow: 0 0 0.125rem rgba(0, 0, 0, 0.25);
|
|
16
16
|
background-color: rgba(255, 255, 255, 0.5);
|
|
17
17
|
border-radius: 0.125rem;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
[part="button"]:disabled {
|
|
21
|
+
opacity: 0.35;
|
|
22
|
+
cursor: default;
|
|
18
23
|
}
|
|
@@ -1,42 +1,52 @@
|
|
|
1
|
-
import { Component, Prop, Part } from '@pictogrammers/element';
|
|
2
|
-
|
|
3
|
-
import template from './treeButtonIcon.html';
|
|
4
|
-
import style from './treeButtonIcon.css';
|
|
5
|
-
import PgIcon from '../icon/icon';
|
|
6
|
-
|
|
7
|
-
const noIcon = 'M0 0h24v24H0V0zm2 2v20h20V2H2z';
|
|
8
|
-
|
|
9
|
-
@Component({
|
|
10
|
-
selector: 'pg-tree-button-icon',
|
|
11
|
-
style,
|
|
12
|
-
template
|
|
13
|
-
})
|
|
14
|
-
export default class PgTreeButtonIcon extends HTMLElement {
|
|
15
|
-
|
|
16
|
-
@Prop() index: number;
|
|
17
|
-
@Prop() icon: string = noIcon;
|
|
18
|
-
|
|
19
|
-
@
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
1
|
+
import { Component, Prop, Part } from '@pictogrammers/element';
|
|
2
|
+
|
|
3
|
+
import template from './treeButtonIcon.html';
|
|
4
|
+
import style from './treeButtonIcon.css';
|
|
5
|
+
import PgIcon from '../icon/icon';
|
|
6
|
+
|
|
7
|
+
const noIcon = 'M0 0h24v24H0V0zm2 2v20h20V2H2z';
|
|
8
|
+
|
|
9
|
+
@Component({
|
|
10
|
+
selector: 'pg-tree-button-icon',
|
|
11
|
+
style,
|
|
12
|
+
template
|
|
13
|
+
})
|
|
14
|
+
export default class PgTreeButtonIcon extends HTMLElement {
|
|
15
|
+
|
|
16
|
+
@Prop() index: number;
|
|
17
|
+
@Prop() icon: string = noIcon;
|
|
18
|
+
@Prop() enabled: boolean = false;
|
|
19
|
+
@Prop() label: string = '';
|
|
20
|
+
|
|
21
|
+
@Part() $button: HTMLButtonElement;
|
|
22
|
+
@Part() $icon: PgIcon;
|
|
23
|
+
|
|
24
|
+
connectedCallback() {
|
|
25
|
+
this.$button.addEventListener('click', this.#handleClick.bind(this));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
#handleClick() {
|
|
29
|
+
if (!this.enabled) return;
|
|
30
|
+
this.dispatchEvent(new CustomEvent('action', {
|
|
31
|
+
bubbles: true,
|
|
32
|
+
composed: true,
|
|
33
|
+
detail: {
|
|
34
|
+
index: this.index
|
|
35
|
+
}
|
|
36
|
+
}));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
render(changes) {
|
|
40
|
+
if (changes.icon) {
|
|
41
|
+
this.$icon.path = this.icon;
|
|
42
|
+
}
|
|
43
|
+
if (changes.enabled) {
|
|
44
|
+
this.$button.disabled = !this.enabled;
|
|
45
|
+
}
|
|
46
|
+
if (changes.label) {
|
|
47
|
+
this.$button.title = this.label;
|
|
48
|
+
this.$button.setAttribute('aria-label', this.label);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
}
|