@pictogrammers/components 0.5.21 → 0.5.23
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/inputPixelEditor/inputPixelEditor.ts +32 -0
- package/pg/inputSelect/__examples__/basic/basic.ts +7 -1
- package/pg/jsonBoolean/__examples__/basic/basic.ts +28 -0
- package/pg/jsonNumber/__examples__/basic/basic.ts +28 -0
- package/pg/jsonString/__examples__/basic/basic.ts +28 -0
- package/pg/menu/menu.css +2 -0
- package/pg/modalAlert/modalAlert.css +1 -18
- package/pg/modalAlert/modalAlert.html +6 -6
- package/pg/modalAlert/modalAlert.ts +5 -1
- package/pg/modalPrompt/README.md +34 -0
- package/pg/modalPrompt/__examples__/basic/basic.html +4 -0
- package/pg/modalPrompt/__examples__/basic/basic.ts +33 -0
- package/pg/modalPrompt/index.ts +3 -0
- package/pg/modalPrompt/modalPrompt.css +32 -0
- package/pg/modalPrompt/modalPrompt.html +23 -0
- package/pg/modalPrompt/modalPrompt.ts +89 -0
- package/pg/node/__examples__/basic/basic.html +9 -0
- package/pg/node/__examples__/basic/basic.ts +3 -1
- package/pg/node/node.ts +31 -8
- package/pg/nodeEditorLink/nodeEditorLink.ts +66 -0
- package/pg/nodeEditorRange/nodeEditorRange.html +28 -0
- package/pg/nodeEditorRange/nodeEditorRange.ts +27 -10
- package/pg/nodeEditorText/nodeEditorText.ts +58 -57
- package/pg/nodeEditorTextArray/nodeEditorTextArray.ts +12 -3
- package/pg/nodes/README.md +2 -0
- package/pg/nodes/__examples__/basic/basic.ts +51 -9
- package/pg/overlayMenu/overlayMenu.css +9 -0
- package/pg/overlayMenu/overlayMenu.ts +18 -0
- package/pg/overlaySelectMenu/overlaySelectMenu.css +5 -1
- package/pg/overlaySelectMenu/overlaySelectMenu.ts +19 -1
- package/pg/tree/README.md +1 -0
- package/pg/tree/__examples__/basic/basic.ts +7 -7
- package/pg/treeItem/treeItem.ts +3 -1
package/package.json
CHANGED
|
@@ -304,7 +304,39 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
304
304
|
this.#updateGrid();
|
|
305
305
|
}
|
|
306
306
|
|
|
307
|
+
/**
|
|
308
|
+
* Clip a cached grid so its dimensions never exceed the current
|
|
309
|
+
* width/height. Used when the canvas is made smaller.
|
|
310
|
+
* @param grid 2d grid to clip in place
|
|
311
|
+
*/
|
|
312
|
+
#clipGrid(grid: number[][]) {
|
|
313
|
+
if (grid.length > this.height) {
|
|
314
|
+
grid.length = this.height;
|
|
315
|
+
}
|
|
316
|
+
for (let y = 0; y < grid.length; y++) {
|
|
317
|
+
if (grid[y].length > this.width) {
|
|
318
|
+
grid[y].length = this.width;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
307
323
|
#redraw() {
|
|
324
|
+
// When the canvas shrinks, clip the cached grids so stale rows/columns
|
|
325
|
+
// outside the new bounds are dropped (the grow case is handled below).
|
|
326
|
+
this.#clipGrid(this.#export);
|
|
327
|
+
this.#clipGrid(this.#selection);
|
|
328
|
+
this.#clipGrid(this.#selectionPreview);
|
|
329
|
+
// Drop selection pixels that now fall outside the canvas so the
|
|
330
|
+
// selection map stays in sync with the clipped #selection grid.
|
|
331
|
+
this.#selectionPixels.forEach(([x, y], key) => {
|
|
332
|
+
if (x >= this.width || y >= this.height) {
|
|
333
|
+
this.#selectionPixels.delete(key);
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
if (this.#selectionPixels.size === 0) {
|
|
337
|
+
this.$selectionPath.classList.toggle('hide', true);
|
|
338
|
+
}
|
|
339
|
+
|
|
308
340
|
// Render individual pixels
|
|
309
341
|
const data = this.#data.toReversed();
|
|
310
342
|
const layerCount = data.length;
|
|
@@ -17,7 +17,13 @@ export default class XPgInputSelectBasic extends HTMLElement {
|
|
|
17
17
|
{ label: 'Option 1', value: 'option1' },
|
|
18
18
|
{ label: 'Option 2', value: 'option2' },
|
|
19
19
|
{ label: 'Option 3', value: 'option3' },
|
|
20
|
-
{ label: 'Option 4', value: 'option4' }
|
|
20
|
+
{ label: 'Option 4', value: 'option4' },
|
|
21
|
+
{ label: 'Option 5', value: 'option5' },
|
|
22
|
+
{ label: 'Option 6', value: 'option6' },
|
|
23
|
+
{ label: 'Option 7', value: 'option7' },
|
|
24
|
+
{ label: 'Option 8', value: 'option8' },
|
|
25
|
+
{ label: 'Option 9', value: 'option9' },
|
|
26
|
+
{ label: 'Option 10', value: 'option10' }
|
|
21
27
|
);
|
|
22
28
|
this.$input.default = { label: 'None', value: '', disabled: true };
|
|
23
29
|
//this.$input.value = this.$input.options[1].value;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Component, Part, Prop } from '@pictogrammers/element';
|
|
2
|
+
|
|
3
|
+
import PgJson from '../../jsonBoolean';
|
|
4
|
+
|
|
5
|
+
import template from './basic.html';
|
|
6
|
+
|
|
7
|
+
@Component({
|
|
8
|
+
selector: 'x-pg-json-boolean-basic',
|
|
9
|
+
template
|
|
10
|
+
})
|
|
11
|
+
export default class XPgJsonBasic extends HTMLElement {
|
|
12
|
+
@Part() $json: PgJson;
|
|
13
|
+
|
|
14
|
+
connectedCallback() {
|
|
15
|
+
this.$json.value = {
|
|
16
|
+
users: [{
|
|
17
|
+
name: 'Dipper Pines',
|
|
18
|
+
age: 12,
|
|
19
|
+
}]
|
|
20
|
+
}; // Array or Object
|
|
21
|
+
this.$json.addEventListener('change', (e: any) => {
|
|
22
|
+
const { parent, key, path, value } = e.detail;
|
|
23
|
+
if (value !== parent[key]) {
|
|
24
|
+
parent[key] = value;
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Component, Part, Prop } from '@pictogrammers/element';
|
|
2
|
+
|
|
3
|
+
import PgJson from '../../jsonNumber';
|
|
4
|
+
|
|
5
|
+
import template from './basic.html';
|
|
6
|
+
|
|
7
|
+
@Component({
|
|
8
|
+
selector: 'x-pg-json-number-basic',
|
|
9
|
+
template
|
|
10
|
+
})
|
|
11
|
+
export default class XPgJsonBasic extends HTMLElement {
|
|
12
|
+
@Part() $json: PgJson;
|
|
13
|
+
|
|
14
|
+
connectedCallback() {
|
|
15
|
+
this.$json.value = {
|
|
16
|
+
users: [{
|
|
17
|
+
name: 'Dipper Pines',
|
|
18
|
+
age: 12,
|
|
19
|
+
}]
|
|
20
|
+
}; // Array or Object
|
|
21
|
+
this.$json.addEventListener('change', (e: any) => {
|
|
22
|
+
const { parent, key, path, value } = e.detail;
|
|
23
|
+
if (value !== parent[key]) {
|
|
24
|
+
parent[key] = value;
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Component, Part, Prop } from '@pictogrammers/element';
|
|
2
|
+
|
|
3
|
+
import PgJson from '../../jsonString';
|
|
4
|
+
|
|
5
|
+
import template from './basic.html';
|
|
6
|
+
|
|
7
|
+
@Component({
|
|
8
|
+
selector: 'x-pg-json-string-basic',
|
|
9
|
+
template
|
|
10
|
+
})
|
|
11
|
+
export default class XPgJsonBasic extends HTMLElement {
|
|
12
|
+
@Part() $json: PgJson;
|
|
13
|
+
|
|
14
|
+
connectedCallback() {
|
|
15
|
+
this.$json.value = {
|
|
16
|
+
users: [{
|
|
17
|
+
name: 'Dipper Pines',
|
|
18
|
+
age: 12,
|
|
19
|
+
}]
|
|
20
|
+
}; // Array or Object
|
|
21
|
+
this.$json.addEventListener('change', (e: any) => {
|
|
22
|
+
const { parent, key, path, value } = e.detail;
|
|
23
|
+
if (value !== parent[key]) {
|
|
24
|
+
parent[key] = value;
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
package/pg/menu/menu.css
CHANGED
|
@@ -16,25 +16,8 @@
|
|
|
16
16
|
overflow: hidden;
|
|
17
17
|
min-width: 15rem;
|
|
18
18
|
}
|
|
19
|
-
|
|
20
|
-
border-bottom: 1px solid #ccc;
|
|
21
|
-
background: #f1f1f1;
|
|
22
|
-
padding: 0.75rem 1rem;
|
|
23
|
-
}
|
|
24
|
-
header h2 {
|
|
19
|
+
[part="headerText"] {
|
|
25
20
|
font-size: 1.25rem;
|
|
26
21
|
margin: 0;
|
|
27
22
|
font-weight: normal;
|
|
28
|
-
}
|
|
29
|
-
main {
|
|
30
|
-
padding: 0.5rem 1rem;
|
|
31
|
-
}
|
|
32
|
-
footer {
|
|
33
|
-
display: flex;
|
|
34
|
-
flex-direction: row;
|
|
35
|
-
padding: 0.75rem 1rem;
|
|
36
|
-
border-top: 1px solid #ccc;
|
|
37
|
-
background: #f1f1f1;
|
|
38
|
-
justify-content: flex-end;
|
|
39
|
-
gap: 0.5rem;
|
|
40
23
|
}
|
|
@@ -4,18 +4,18 @@
|
|
|
4
4
|
id="dialog1"
|
|
5
5
|
aria-labelledby="dialog_label"
|
|
6
6
|
aria-modal="true">
|
|
7
|
-
<header part="header">
|
|
7
|
+
<pg-modal-header part="header">
|
|
8
8
|
<h2 id="dialog_label"
|
|
9
9
|
class="dialog_label"
|
|
10
10
|
part="headerText">
|
|
11
11
|
Alert
|
|
12
12
|
</h2>
|
|
13
|
-
</header>
|
|
14
|
-
<
|
|
13
|
+
</pg-modal-header>
|
|
14
|
+
<pg-modal-body>
|
|
15
15
|
<p part="message"></p>
|
|
16
|
-
</
|
|
17
|
-
<footer>
|
|
16
|
+
</pg-modal-body>
|
|
17
|
+
<pg-modal-footer>
|
|
18
18
|
<pg-button part="okay" variant="brand">Okay</pg-button>
|
|
19
|
-
</footer>
|
|
19
|
+
</pg-modal-footer>
|
|
20
20
|
</div>
|
|
21
21
|
</div>
|
|
@@ -5,6 +5,10 @@ import style from './modalAlert.css';
|
|
|
5
5
|
|
|
6
6
|
import PgOverlay from '../overlay/overlay';
|
|
7
7
|
import PgButton from '../button/button';
|
|
8
|
+
import PgModalHeader from '../modalHeader/modalHeader';
|
|
9
|
+
|
|
10
|
+
import '../modalBody/modalBody';
|
|
11
|
+
import '../modalFooter/modalFooter';
|
|
8
12
|
|
|
9
13
|
@Component({
|
|
10
14
|
selector: 'pg-modal-alert',
|
|
@@ -15,7 +19,7 @@ export default class PgModalAlert extends PgOverlay {
|
|
|
15
19
|
@Prop() header: string = 'Are you sure?';
|
|
16
20
|
@Prop() message: string = 'Are you sure?';
|
|
17
21
|
|
|
18
|
-
@Part() $header:
|
|
22
|
+
@Part() $header: PgModalHeader;
|
|
19
23
|
@Part() $headerText: HTMLHeadingElement;
|
|
20
24
|
@Part() $message: HTMLDivElement;
|
|
21
25
|
@Part() $okay: PgButton;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# `PgModalPrompt`
|
|
2
|
+
|
|
3
|
+
The `PgModalPrompt` creates a dialog with a single text field, an okay button, and a cancel button. For a message only dialog use `PgModalAlert`. For okay/cancel without a field use `PgModalConfirm`.
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
import PgModalPrompt from '@pictogrammers/components/pg/modalPrompt';
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
const result = await PgModalPrompt.open({
|
|
11
|
+
header: 'Rename Item',
|
|
12
|
+
message: 'Enter a new name for the item.',
|
|
13
|
+
value: 'Untitled',
|
|
14
|
+
placeholder: 'Name',
|
|
15
|
+
});
|
|
16
|
+
if (result === null) {
|
|
17
|
+
console.log('Cancelled');
|
|
18
|
+
} else {
|
|
19
|
+
console.log('New name:', result);
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Props
|
|
24
|
+
|
|
25
|
+
| Props | Default | Description |
|
|
26
|
+
| ----------- | --------------------------- | ----------- |
|
|
27
|
+
| header | `'Please provide a value'` | Dialog title |
|
|
28
|
+
| message | `''` | Optional message shown above the field |
|
|
29
|
+
| value | `''` | Initial value of the field |
|
|
30
|
+
| placeholder | `''` | Placeholder text for the field |
|
|
31
|
+
| okay | `'Okay'` | Okay button label |
|
|
32
|
+
| cancel | `'Cancel'` | Cancel button label |
|
|
33
|
+
|
|
34
|
+
`close()` resolves with the field's value when `Okay` is clicked (or `Enter` is pressed), and `null` when `Cancel` is clicked or `Escape` is pressed.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Component, Part } from '@pictogrammers/element';
|
|
2
|
+
import PgModalPrompt from '../../modalPrompt';
|
|
3
|
+
|
|
4
|
+
import template from './basic.html';
|
|
5
|
+
|
|
6
|
+
@Component({
|
|
7
|
+
selector: 'x-pg-modal-prompt-basic',
|
|
8
|
+
template
|
|
9
|
+
})
|
|
10
|
+
export default class XPgModalPromptBasic extends HTMLElement {
|
|
11
|
+
|
|
12
|
+
@Part() $button: HTMLButtonElement;
|
|
13
|
+
@Part() $result: HTMLSpanElement;
|
|
14
|
+
|
|
15
|
+
currentValue = 'Hello World!';
|
|
16
|
+
|
|
17
|
+
connectedCallback() {
|
|
18
|
+
this.$result.textContent = `${this.currentValue}`;
|
|
19
|
+
this.$button.addEventListener('click', this.handleClick.bind(this));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async handleClick() {
|
|
23
|
+
const result = await PgModalPrompt.open({
|
|
24
|
+
header: 'Rename Item',
|
|
25
|
+
message: 'Enter a new name for the item.',
|
|
26
|
+
value: this.currentValue,
|
|
27
|
+
placeholder: 'Name',
|
|
28
|
+
});
|
|
29
|
+
this.currentValue = result;
|
|
30
|
+
this.$result.textContent = `${result}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
.backdrop {
|
|
2
|
+
display: flex;
|
|
3
|
+
position: fixed;
|
|
4
|
+
top: 0;
|
|
5
|
+
left: 0;
|
|
6
|
+
right: 0;
|
|
7
|
+
bottom: 0;
|
|
8
|
+
background: rgba(0, 0, 0, 0.6);
|
|
9
|
+
justify-content: center;
|
|
10
|
+
align-items: center;
|
|
11
|
+
}
|
|
12
|
+
.dialog {
|
|
13
|
+
background: #fff;
|
|
14
|
+
border-radius: 0.5rem;
|
|
15
|
+
box-shadow: 0 1px 1rem rgba(0, 0, 0, 0.5);
|
|
16
|
+
overflow: hidden;
|
|
17
|
+
min-width: 15rem;
|
|
18
|
+
}
|
|
19
|
+
[part="headerText"] {
|
|
20
|
+
font-size: 1.25rem;
|
|
21
|
+
margin: 0;
|
|
22
|
+
font-weight: normal;
|
|
23
|
+
}
|
|
24
|
+
[part="message"] {
|
|
25
|
+
margin-block: 0.5rem 1rem;
|
|
26
|
+
}
|
|
27
|
+
[part="message"]:empty {
|
|
28
|
+
display: none;
|
|
29
|
+
}
|
|
30
|
+
[part="input"] {
|
|
31
|
+
margin-block-end: 0.5rem;
|
|
32
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<div class="backdrop">
|
|
2
|
+
<div class="dialog"
|
|
3
|
+
role="dialog"
|
|
4
|
+
id="dialog1"
|
|
5
|
+
aria-labelledby="dialog_label"
|
|
6
|
+
aria-modal="true">
|
|
7
|
+
<pg-modal-header part="header">
|
|
8
|
+
<h2 id="dialog_label"
|
|
9
|
+
class="dialog_label"
|
|
10
|
+
part="headerText">
|
|
11
|
+
Prompt
|
|
12
|
+
</h2>
|
|
13
|
+
</pg-modal-header>
|
|
14
|
+
<pg-modal-body>
|
|
15
|
+
<p part="message"></p>
|
|
16
|
+
<pg-input-text part="input"></pg-input-text>
|
|
17
|
+
</pg-modal-body>
|
|
18
|
+
<pg-modal-footer>
|
|
19
|
+
<pg-button part="cancel">Cancel</pg-button>
|
|
20
|
+
<pg-button part="okay" variant="brand">Okay</pg-button>
|
|
21
|
+
</pg-modal-footer>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { Component, Prop, Part } from '@pictogrammers/element';
|
|
2
|
+
|
|
3
|
+
import template from './modalPrompt.html';
|
|
4
|
+
import style from './modalPrompt.css';
|
|
5
|
+
|
|
6
|
+
import PgOverlay from '../overlay/overlay';
|
|
7
|
+
import PgButton from '../button/button';
|
|
8
|
+
import PgInputText from '../inputText/inputText';
|
|
9
|
+
import PgModalHeader from '../modalHeader/modalHeader';
|
|
10
|
+
|
|
11
|
+
import '../modalBody/modalBody';
|
|
12
|
+
import '../modalFooter/modalFooter';
|
|
13
|
+
|
|
14
|
+
@Component({
|
|
15
|
+
selector: 'pg-modal-prompt',
|
|
16
|
+
template,
|
|
17
|
+
style
|
|
18
|
+
})
|
|
19
|
+
export default class PgModalPrompt extends PgOverlay {
|
|
20
|
+
@Prop() header: string = 'Please provide a value';
|
|
21
|
+
@Prop() message: string = '';
|
|
22
|
+
@Prop() value: string = '';
|
|
23
|
+
@Prop() placeholder: string = '';
|
|
24
|
+
@Prop() cancel: string = 'Cancel';
|
|
25
|
+
@Prop() okay: string = 'Okay';
|
|
26
|
+
|
|
27
|
+
@Part() $header: PgModalHeader;
|
|
28
|
+
@Part() $headerText: HTMLHeadingElement;
|
|
29
|
+
@Part() $message: HTMLParagraphElement;
|
|
30
|
+
@Part() $input: PgInputText;
|
|
31
|
+
@Part() $okay: PgButton;
|
|
32
|
+
@Part() $cancel: PgButton;
|
|
33
|
+
|
|
34
|
+
#cacheKeydownHandler: any;
|
|
35
|
+
|
|
36
|
+
connectedCallback() {
|
|
37
|
+
this.$okay.addEventListener('click', this.#handleOkay.bind(this));
|
|
38
|
+
this.$cancel.addEventListener('click', this.#handleCancel.bind(this));
|
|
39
|
+
this.$input.addEventListener('keydown', this.#handleInputKeyDown.bind(this));
|
|
40
|
+
this.#cacheKeydownHandler ??= this.#handleKeyDown.bind(this);
|
|
41
|
+
document.addEventListener('keydown', this.#cacheKeydownHandler);
|
|
42
|
+
this.$input.focus();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
disconnectedCallback() {
|
|
46
|
+
document.removeEventListener('keydown', this.#cacheKeydownHandler);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
#handleKeyDown(e: KeyboardEvent) {
|
|
50
|
+
if (e.key === 'Escape') {
|
|
51
|
+
this.close(null);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
#handleInputKeyDown(e: KeyboardEvent) {
|
|
56
|
+
if (e.key === 'Enter') {
|
|
57
|
+
this.#handleOkay();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
#handleOkay() {
|
|
62
|
+
this.close(this.$input.value);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
#handleCancel() {
|
|
66
|
+
this.close(null);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
render(changes) {
|
|
70
|
+
if (changes.header) {
|
|
71
|
+
this.$headerText.textContent = this.header;
|
|
72
|
+
}
|
|
73
|
+
if (changes.message) {
|
|
74
|
+
this.$message.textContent = this.message;
|
|
75
|
+
}
|
|
76
|
+
if (changes.value) {
|
|
77
|
+
this.$input.value = this.value;
|
|
78
|
+
}
|
|
79
|
+
if (changes.placeholder) {
|
|
80
|
+
this.$input.placeholder = this.placeholder;
|
|
81
|
+
}
|
|
82
|
+
if (changes.okay) {
|
|
83
|
+
this.$okay.textContent = this.okay;
|
|
84
|
+
}
|
|
85
|
+
if (changes.cancel) {
|
|
86
|
+
this.$cancel.textContent = this.cancel;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -2,6 +2,7 @@ import { Component, Part } from '@pictogrammers/element';
|
|
|
2
2
|
import PgNode from '../../node';
|
|
3
3
|
|
|
4
4
|
import template from './basic.html';
|
|
5
|
+
import PgNodeEditorText from 'components/pg/nodeEditorText/nodeEditorText';
|
|
5
6
|
|
|
6
7
|
@Component({
|
|
7
8
|
selector: 'x-pg-node-basic',
|
|
@@ -12,11 +13,12 @@ export default class XPgNodeBasic extends HTMLElement {
|
|
|
12
13
|
@Part() $node: PgNode;
|
|
13
14
|
|
|
14
15
|
connectedCallback() {
|
|
16
|
+
this.$node.editors.push(PgNodeEditorText);
|
|
15
17
|
this.$node.fields = [{
|
|
16
18
|
label: 'Name',
|
|
17
19
|
value: 'foo',
|
|
18
20
|
name: 'name',
|
|
19
|
-
type: '
|
|
21
|
+
type: 'Text',
|
|
20
22
|
}];
|
|
21
23
|
this.$node.addEventListener('change', this.#handleChange.bind(this));
|
|
22
24
|
this.$node.addEventListener('input', this.#handleInput.bind(this));
|
package/pg/node/node.ts
CHANGED
|
@@ -40,7 +40,11 @@ export default class PgNode extends HTMLElement {
|
|
|
40
40
|
container: this.$items,
|
|
41
41
|
items: this.fields,
|
|
42
42
|
type: (item) => {
|
|
43
|
-
|
|
43
|
+
const comp = this.editors.find(x => x.type === item.type);
|
|
44
|
+
if (!comp) {
|
|
45
|
+
throw new Error(`"${item.name}" missing in editors array.`);
|
|
46
|
+
}
|
|
47
|
+
return comp;
|
|
44
48
|
},
|
|
45
49
|
create: ($item: any, item) => {
|
|
46
50
|
this.height += $item.height;
|
|
@@ -54,6 +58,14 @@ export default class PgNode extends HTMLElement {
|
|
|
54
58
|
this.#syncFieldHeight(item.itemKey, $item);
|
|
55
59
|
this.#dispatchArg('change', item.itemKey, e.detail.value);
|
|
56
60
|
});
|
|
61
|
+
// Editor events don't compose past this shadow root; proxy the
|
|
62
|
+
// Link editor's events up for pg-nodes.
|
|
63
|
+
$item.addEventListener('nodepulse', (e: any) => {
|
|
64
|
+
this.dispatchEvent(new CustomEvent('nodepulse', { detail: e.detail }));
|
|
65
|
+
});
|
|
66
|
+
$item.addEventListener('nodeselection', (e: any) => {
|
|
67
|
+
this.dispatchEvent(new CustomEvent('nodeselection', { detail: e.detail }));
|
|
68
|
+
});
|
|
57
69
|
},
|
|
58
70
|
});
|
|
59
71
|
|
|
@@ -67,10 +79,12 @@ export default class PgNode extends HTMLElement {
|
|
|
67
79
|
this.height += $item.height;
|
|
68
80
|
},
|
|
69
81
|
connect: ($item: any, item) => {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
82
|
+
let height = 0;
|
|
83
|
+
this.#fieldHeights.forEach((value) => {
|
|
84
|
+
height += value;
|
|
85
|
+
});
|
|
86
|
+
const index = Array.from(this.$outputs.children).indexOf($item);
|
|
87
|
+
this.#registerOutputPin(item.key, item.label, ((height + index + 2) * 16) + 5);
|
|
74
88
|
},
|
|
75
89
|
});
|
|
76
90
|
this.#intrinsicHeight = this.height;
|
|
@@ -78,6 +92,11 @@ export default class PgNode extends HTMLElement {
|
|
|
78
92
|
this.height = requestedHeight;
|
|
79
93
|
}
|
|
80
94
|
this.$node.addEventListener('pointerover', this.#handlePointerOver.bind(this));
|
|
95
|
+
this.$node.addEventListener('animationend', (e: AnimationEvent) => {
|
|
96
|
+
if (e.animationName === 'pg-node-pulse') {
|
|
97
|
+
this.$node.classList.remove('pulse');
|
|
98
|
+
}
|
|
99
|
+
});
|
|
81
100
|
}
|
|
82
101
|
|
|
83
102
|
// Editor heights are cached by field key so a change event only triggers a
|
|
@@ -262,9 +281,13 @@ export default class PgNode extends HTMLElement {
|
|
|
262
281
|
}
|
|
263
282
|
}
|
|
264
283
|
|
|
284
|
+
// Blue attention pulse (3 flashes); the animationend listener added in
|
|
285
|
+
// connectedCallback clears the class so it can run again later.
|
|
265
286
|
pulse() {
|
|
266
|
-
|
|
267
|
-
//
|
|
268
|
-
|
|
287
|
+
this.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' });
|
|
288
|
+
// Restart the animation when a pulse is already running.
|
|
289
|
+
this.$node.classList.remove('pulse');
|
|
290
|
+
void this.$node.offsetWidth;
|
|
291
|
+
this.$node.classList.add('pulse');
|
|
269
292
|
}
|
|
270
293
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Component, Prop, Part } from '@pictogrammers/element';
|
|
2
|
+
|
|
3
|
+
import template from './nodeEditorLink.html';
|
|
4
|
+
import style from './nodeEditorLink.css';
|
|
5
|
+
|
|
6
|
+
@Component({
|
|
7
|
+
selector: 'pg-node-editor-link',
|
|
8
|
+
style,
|
|
9
|
+
template,
|
|
10
|
+
})
|
|
11
|
+
export default class PgNodeEditorLink extends HTMLElement {
|
|
12
|
+
|
|
13
|
+
static type = 'Link';
|
|
14
|
+
|
|
15
|
+
@Prop() label: string = '';
|
|
16
|
+
@Prop() value: number = 0;
|
|
17
|
+
@Prop() name: string = '';
|
|
18
|
+
|
|
19
|
+
@Part() $label: HTMLSpanElement;
|
|
20
|
+
@Part() $link: HTMLButtonElement;
|
|
21
|
+
@Part() $select: HTMLButtonElement;
|
|
22
|
+
|
|
23
|
+
connectedCallback() {
|
|
24
|
+
// Pulse the linked node; pg-node proxies this up to pg-nodes.
|
|
25
|
+
this.$link.addEventListener('click', () => {
|
|
26
|
+
this.dispatchEvent(new CustomEvent('nodepulse', {
|
|
27
|
+
detail: {
|
|
28
|
+
id: this.value,
|
|
29
|
+
}
|
|
30
|
+
}));
|
|
31
|
+
});
|
|
32
|
+
// Ask pg-nodes to enter selection mode; it calls back with the picked id.
|
|
33
|
+
this.$select.addEventListener('click', () => {
|
|
34
|
+
this.dispatchEvent(new CustomEvent('nodeselection', {
|
|
35
|
+
detail: {
|
|
36
|
+
value: this.value,
|
|
37
|
+
callback: (nodeId: number) => {
|
|
38
|
+
this.value = nodeId;
|
|
39
|
+
this.dispatchEvent(new CustomEvent('change', {
|
|
40
|
+
detail: {
|
|
41
|
+
value: nodeId,
|
|
42
|
+
}
|
|
43
|
+
}));
|
|
44
|
+
},
|
|
45
|
+
}
|
|
46
|
+
}));
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
render(changes: any) {
|
|
51
|
+
if (changes.label) {
|
|
52
|
+
this.$label.textContent = this.label;
|
|
53
|
+
}
|
|
54
|
+
if (changes.value) {
|
|
55
|
+
this.$link.textContent = `Node ${this.value}`;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
get height() {
|
|
60
|
+
return 2;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
focus() {
|
|
64
|
+
this.$link.focus();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<label>
|
|
2
|
+
<span part="label"></span>
|
|
3
|
+
<input type="number" part="min">
|
|
4
|
+
</label>
|
|
5
|
+
<button>
|
|
6
|
+
<svg viewBox="0 0 24 24">
|
|
7
|
+
<path d="M19,13H5V11H19V13Z" />
|
|
8
|
+
</svg>
|
|
9
|
+
</button>
|
|
10
|
+
<button>
|
|
11
|
+
<svg viewBox="0 0 24 24">
|
|
12
|
+
<path d="M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z" />
|
|
13
|
+
</svg>
|
|
14
|
+
</button>
|
|
15
|
+
<label>
|
|
16
|
+
<span part="label"></span>
|
|
17
|
+
<input type="range" part="max">
|
|
18
|
+
</label>
|
|
19
|
+
<button>
|
|
20
|
+
<svg viewBox="0 0 24 24">
|
|
21
|
+
<path d="M19,13H5V11H19V13Z" />
|
|
22
|
+
</svg>
|
|
23
|
+
</button>
|
|
24
|
+
<button>
|
|
25
|
+
<svg viewBox="0 0 24 24">
|
|
26
|
+
<path d="M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z" />
|
|
27
|
+
</svg>
|
|
28
|
+
</button>
|
|
@@ -20,22 +20,39 @@ export default class PgNodeEditorRange extends HTMLElement {
|
|
|
20
20
|
@Prop() step: number = 1;
|
|
21
21
|
|
|
22
22
|
@Part() $label: HTMLLabelElement;
|
|
23
|
-
@Part() $
|
|
23
|
+
@Part() $min: HTMLInputElement;
|
|
24
|
+
@Part() $max: HTMLInputElement;
|
|
24
25
|
|
|
25
26
|
connectedCallback() {
|
|
26
|
-
this.$
|
|
27
|
+
this.$min.addEventListener('change', (e: any) => {
|
|
27
28
|
e.stopPropagation();
|
|
28
29
|
this.dispatchEvent(new CustomEvent('change', {
|
|
29
30
|
detail: {
|
|
30
|
-
value: Number(this.$
|
|
31
|
+
value: Number(this.$min.value),
|
|
31
32
|
}
|
|
32
33
|
}));
|
|
33
34
|
});
|
|
34
|
-
this.$
|
|
35
|
+
this.$min.addEventListener('input', (e: any) => {
|
|
35
36
|
e.stopPropagation();
|
|
36
37
|
this.dispatchEvent(new CustomEvent('input', {
|
|
37
38
|
detail: {
|
|
38
|
-
value: Number(this.$
|
|
39
|
+
value: Number(this.$min.value),
|
|
40
|
+
}
|
|
41
|
+
}));
|
|
42
|
+
});
|
|
43
|
+
this.$max.addEventListener('change', (e: any) => {
|
|
44
|
+
e.stopPropagation();
|
|
45
|
+
this.dispatchEvent(new CustomEvent('change', {
|
|
46
|
+
detail: {
|
|
47
|
+
value: Number(this.$max.value),
|
|
48
|
+
}
|
|
49
|
+
}));
|
|
50
|
+
});
|
|
51
|
+
this.$max.addEventListener('input', (e: any) => {
|
|
52
|
+
e.stopPropagation();
|
|
53
|
+
this.dispatchEvent(new CustomEvent('input', {
|
|
54
|
+
detail: {
|
|
55
|
+
value: Number(this.$max.value),
|
|
39
56
|
}
|
|
40
57
|
}));
|
|
41
58
|
});
|
|
@@ -46,16 +63,16 @@ export default class PgNodeEditorRange extends HTMLElement {
|
|
|
46
63
|
this.$label.textContent = this.label;
|
|
47
64
|
}
|
|
48
65
|
if (changes.value) {
|
|
49
|
-
this.$
|
|
66
|
+
this.$min.value = String(this.value);
|
|
50
67
|
}
|
|
51
68
|
if (changes.min) {
|
|
52
|
-
this.$
|
|
69
|
+
this.$min.min = String(this.min);
|
|
53
70
|
}
|
|
54
71
|
if (changes.max) {
|
|
55
|
-
this.$
|
|
72
|
+
this.$min.max = String(this.max);
|
|
56
73
|
}
|
|
57
74
|
if (changes.step) {
|
|
58
|
-
this.$
|
|
75
|
+
this.$min.step = String(this.step);
|
|
59
76
|
}
|
|
60
77
|
}
|
|
61
78
|
|
|
@@ -64,6 +81,6 @@ export default class PgNodeEditorRange extends HTMLElement {
|
|
|
64
81
|
}
|
|
65
82
|
|
|
66
83
|
focus() {
|
|
67
|
-
this.$
|
|
84
|
+
this.$min.focus();
|
|
68
85
|
}
|
|
69
86
|
}
|
|
@@ -1,57 +1,58 @@
|
|
|
1
|
-
import { Component, Prop, Part } from '@pictogrammers/element';
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
@Prop()
|
|
17
|
-
@Prop()
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
@Part() $
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
1
|
+
import { Component, Prop, Part } from '@pictogrammers/element';
|
|
2
|
+
|
|
3
|
+
import PgNodeInputText from '../nodeInputText/nodeInputText';
|
|
4
|
+
import template from './nodeEditorText.html';
|
|
5
|
+
import style from './nodeEditorText.css';
|
|
6
|
+
|
|
7
|
+
@Component({
|
|
8
|
+
selector: 'pg-node-editor-text',
|
|
9
|
+
style,
|
|
10
|
+
template,
|
|
11
|
+
})
|
|
12
|
+
export default class PgNodeEditorText extends HTMLElement {
|
|
13
|
+
|
|
14
|
+
static type = 'Text';
|
|
15
|
+
|
|
16
|
+
@Prop() label: string = '';
|
|
17
|
+
@Prop() value: string = '';
|
|
18
|
+
@Prop() name: string = '';
|
|
19
|
+
|
|
20
|
+
@Part() $label: HTMLSpanElement;
|
|
21
|
+
@Part() $input: PgNodeInputText;
|
|
22
|
+
|
|
23
|
+
connectedCallback() {
|
|
24
|
+
this.$input.addEventListener('change', (e: any) => {
|
|
25
|
+
e.stopPropagation();
|
|
26
|
+
this.dispatchEvent(new CustomEvent('change', {
|
|
27
|
+
detail: {
|
|
28
|
+
value: e.detail.value,
|
|
29
|
+
}
|
|
30
|
+
}));
|
|
31
|
+
});
|
|
32
|
+
this.$input.addEventListener('input', (e: any) => {
|
|
33
|
+
e.stopPropagation();
|
|
34
|
+
this.dispatchEvent(new CustomEvent('input', {
|
|
35
|
+
detail: {
|
|
36
|
+
value: e.detail.value,
|
|
37
|
+
}
|
|
38
|
+
}));
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
render(changes: any) {
|
|
43
|
+
if (changes.label) {
|
|
44
|
+
this.$label.textContent = this.label;
|
|
45
|
+
}
|
|
46
|
+
if (changes.value) {
|
|
47
|
+
this.$input.value = this.value;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
get height() {
|
|
52
|
+
return 2;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
focus() {
|
|
56
|
+
this.$input.focus();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -110,6 +110,12 @@ export default class PgNodeEditorTextArray extends HTMLElement {
|
|
|
110
110
|
key: uuid(),
|
|
111
111
|
value: '',
|
|
112
112
|
});
|
|
113
|
+
// Emit updated array
|
|
114
|
+
this.dispatchEvent(new CustomEvent('change', {
|
|
115
|
+
detail: {
|
|
116
|
+
value: this.#inputs.map(x => x.value),
|
|
117
|
+
},
|
|
118
|
+
}));
|
|
113
119
|
});
|
|
114
120
|
$item.addEventListener('remove', (e: any) => {
|
|
115
121
|
const { index } = e.detail;
|
|
@@ -138,9 +144,12 @@ export default class PgNodeEditorTextArray extends HTMLElement {
|
|
|
138
144
|
key: uuid(),
|
|
139
145
|
value: '',
|
|
140
146
|
});
|
|
141
|
-
// Emit updated array
|
|
147
|
+
// Emit updated array (#inputs is the live list; this.value is only the
|
|
148
|
+
// initial prop and goes stale after the first edit)
|
|
142
149
|
this.dispatchEvent(new CustomEvent('change', {
|
|
143
|
-
detail: {
|
|
150
|
+
detail: {
|
|
151
|
+
value: this.#inputs.map(x => x.value),
|
|
152
|
+
},
|
|
144
153
|
}));
|
|
145
154
|
});
|
|
146
155
|
}
|
|
@@ -152,7 +161,7 @@ export default class PgNodeEditorTextArray extends HTMLElement {
|
|
|
152
161
|
}
|
|
153
162
|
|
|
154
163
|
get height() {
|
|
155
|
-
return this.#inputs.length + 1;
|
|
164
|
+
return (this.#inputs.length || 1) + 1;
|
|
156
165
|
}
|
|
157
166
|
|
|
158
167
|
focus() {
|
package/pg/nodes/README.md
CHANGED
|
@@ -39,6 +39,8 @@ import '@pictogrammers/components/pgNodes';
|
|
|
39
39
|
|
|
40
40
|
### Editors
|
|
41
41
|
|
|
42
|
+
Nodes are made of input fields controlled by editor components. Each editor has a `static type = 'Text';` definition unique to the script.
|
|
43
|
+
|
|
42
44
|
```typescript
|
|
43
45
|
this.$script.editors.push(PgNodeEditorText);
|
|
44
46
|
```
|
|
@@ -58,7 +58,7 @@ export default class XPgNodesBasic extends HTMLElement {
|
|
|
58
58
|
this.$script.nodes.push({
|
|
59
59
|
name: 'stateHas',
|
|
60
60
|
label: 'Has',
|
|
61
|
-
width:
|
|
61
|
+
width: 10,
|
|
62
62
|
args: [{
|
|
63
63
|
key: 'key',
|
|
64
64
|
label: 'Key',
|
|
@@ -117,8 +117,13 @@ export default class XPgNodesBasic extends HTMLElement {
|
|
|
117
117
|
}, {
|
|
118
118
|
name: 'stateSet',
|
|
119
119
|
label: 'Set',
|
|
120
|
-
width:
|
|
120
|
+
width: 10,
|
|
121
121
|
args: [{
|
|
122
|
+
key: 'key',
|
|
123
|
+
label: 'Key',
|
|
124
|
+
editor: 'Text',
|
|
125
|
+
value: '',
|
|
126
|
+
}, {
|
|
122
127
|
key: 'value',
|
|
123
128
|
label: 'Value',
|
|
124
129
|
editor: 'Text',
|
|
@@ -142,6 +147,11 @@ export default class XPgNodesBasic extends HTMLElement {
|
|
|
142
147
|
label: 'Equals',
|
|
143
148
|
width: 10,
|
|
144
149
|
args: [{
|
|
150
|
+
key: 'key',
|
|
151
|
+
label: 'Key',
|
|
152
|
+
editor: 'Text',
|
|
153
|
+
value: '',
|
|
154
|
+
}, {
|
|
145
155
|
key: 'value',
|
|
146
156
|
label: 'Value',
|
|
147
157
|
editor: 'Text',
|
|
@@ -170,6 +180,11 @@ export default class XPgNodesBasic extends HTMLElement {
|
|
|
170
180
|
label: 'Less Than',
|
|
171
181
|
width: 10,
|
|
172
182
|
args: [{
|
|
183
|
+
key: 'key',
|
|
184
|
+
label: 'Key',
|
|
185
|
+
editor: 'Text',
|
|
186
|
+
value: '',
|
|
187
|
+
}, {
|
|
173
188
|
key: 'value',
|
|
174
189
|
label: 'Value',
|
|
175
190
|
editor: 'Number',
|
|
@@ -198,6 +213,11 @@ export default class XPgNodesBasic extends HTMLElement {
|
|
|
198
213
|
label: 'Less Than or Equal',
|
|
199
214
|
width: 10,
|
|
200
215
|
args: [{
|
|
216
|
+
key: 'key',
|
|
217
|
+
label: 'Key',
|
|
218
|
+
editor: 'Text',
|
|
219
|
+
value: '',
|
|
220
|
+
}, {
|
|
201
221
|
key: 'value',
|
|
202
222
|
label: 'Value',
|
|
203
223
|
editor: 'Number',
|
|
@@ -226,6 +246,11 @@ export default class XPgNodesBasic extends HTMLElement {
|
|
|
226
246
|
label: 'Greater Than',
|
|
227
247
|
width: 10,
|
|
228
248
|
args: [{
|
|
249
|
+
key: 'key',
|
|
250
|
+
label: 'Key',
|
|
251
|
+
editor: 'Text',
|
|
252
|
+
value: '',
|
|
253
|
+
}, {
|
|
229
254
|
key: 'value',
|
|
230
255
|
label: 'Value',
|
|
231
256
|
editor: 'Number',
|
|
@@ -254,6 +279,11 @@ export default class XPgNodesBasic extends HTMLElement {
|
|
|
254
279
|
label: 'Greater Than or Equal',
|
|
255
280
|
width: 10,
|
|
256
281
|
args: [{
|
|
282
|
+
key: 'key',
|
|
283
|
+
label: 'Key',
|
|
284
|
+
editor: 'Text',
|
|
285
|
+
value: '',
|
|
286
|
+
}, {
|
|
257
287
|
key: 'value',
|
|
258
288
|
label: 'Value',
|
|
259
289
|
editor: 'Number',
|
|
@@ -352,8 +382,13 @@ export default class XPgNodesBasic extends HTMLElement {
|
|
|
352
382
|
}, {
|
|
353
383
|
name: 'isTrue',
|
|
354
384
|
label: 'Is True',
|
|
355
|
-
width:
|
|
356
|
-
args: [
|
|
385
|
+
width: 10,
|
|
386
|
+
args: [{
|
|
387
|
+
key: 'key',
|
|
388
|
+
label: 'Key',
|
|
389
|
+
editor: 'Text',
|
|
390
|
+
value: '',
|
|
391
|
+
}],
|
|
357
392
|
nodes: [{
|
|
358
393
|
key: 't',
|
|
359
394
|
label: 'True',
|
|
@@ -375,8 +410,13 @@ export default class XPgNodesBasic extends HTMLElement {
|
|
|
375
410
|
}, {
|
|
376
411
|
name: 'isFalse',
|
|
377
412
|
label: 'Is False',
|
|
378
|
-
width:
|
|
379
|
-
args: [
|
|
413
|
+
width: 10,
|
|
414
|
+
args: [{
|
|
415
|
+
key: 'key',
|
|
416
|
+
label: 'Key',
|
|
417
|
+
editor: 'Text',
|
|
418
|
+
value: '',
|
|
419
|
+
}],
|
|
380
420
|
nodes: [{
|
|
381
421
|
key: 't',
|
|
382
422
|
label: 'True',
|
|
@@ -556,10 +596,11 @@ export default class XPgNodesBasic extends HTMLElement {
|
|
|
556
596
|
});
|
|
557
597
|
this.$script.items.push({
|
|
558
598
|
id: 2,
|
|
559
|
-
x:
|
|
599
|
+
x: 28,
|
|
560
600
|
y: 2,
|
|
561
601
|
node: 'stateSet',
|
|
562
602
|
args: {
|
|
603
|
+
key: 'health',
|
|
563
604
|
value: '10',
|
|
564
605
|
},
|
|
565
606
|
nodes: {
|
|
@@ -568,10 +609,11 @@ export default class XPgNodesBasic extends HTMLElement {
|
|
|
568
609
|
});
|
|
569
610
|
this.$script.items.push({
|
|
570
611
|
id: 3,
|
|
571
|
-
x:
|
|
612
|
+
x: 28,
|
|
572
613
|
y: 10,
|
|
573
614
|
node: 'stateSet',
|
|
574
615
|
args: {
|
|
616
|
+
key: 'health',
|
|
575
617
|
value: '20',
|
|
576
618
|
},
|
|
577
619
|
nodes: {
|
|
@@ -580,7 +622,7 @@ export default class XPgNodesBasic extends HTMLElement {
|
|
|
580
622
|
});
|
|
581
623
|
this.$script.items.push({
|
|
582
624
|
id: 4,
|
|
583
|
-
x:
|
|
625
|
+
x: 40,
|
|
584
626
|
y: 8,
|
|
585
627
|
node: 'log',
|
|
586
628
|
args: {
|
|
@@ -15,4 +15,13 @@
|
|
|
15
15
|
overflow: visible;
|
|
16
16
|
position-visibility: always;
|
|
17
17
|
position-anchor: auto;
|
|
18
|
+
position-try-fallbacks: --pg-menu-up, --pg-menu-over;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@position-try --pg-menu-up {
|
|
22
|
+
max-height: 10rem;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@position-try --pg-menu-over {
|
|
26
|
+
max-height: 5rem;
|
|
18
27
|
}
|
|
@@ -51,6 +51,24 @@ export default class PgOverlayMenu extends PgOverlay {
|
|
|
51
51
|
if (!this.preventFocus) {
|
|
52
52
|
this.$menu.focus(index);
|
|
53
53
|
}
|
|
54
|
+
// Overlay
|
|
55
|
+
const observer = new IntersectionObserver((entries) => {
|
|
56
|
+
entries.forEach(entry => {
|
|
57
|
+
console.log(entry);
|
|
58
|
+
// entry.isIntersecting will be false if it is entirely outside the viewport
|
|
59
|
+
if (!entry.isIntersecting) {
|
|
60
|
+
console.log('Element is completely outside the viewport');
|
|
61
|
+
} else {
|
|
62
|
+
console.log('Element is at least partially inside the viewport');
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}, {
|
|
66
|
+
trackVisibility: true,
|
|
67
|
+
root: null, // Defaults to the browser viewport
|
|
68
|
+
threshold: 0 // Triggers as soon as even 1 pixel enters or leaves
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
observer.observe(this.$overlay);
|
|
54
72
|
}
|
|
55
73
|
|
|
56
74
|
#toggle(e: ToggleEvent) {
|
|
@@ -77,6 +77,24 @@ export default class PgOverlaySelectMenu extends PgOverlay {
|
|
|
77
77
|
this.$overlay.style.setProperty('--pg-overlay-menu-_y', `${y}px`);
|
|
78
78
|
// Focus
|
|
79
79
|
this.$menu.focus(index);
|
|
80
|
+
// Overlay
|
|
81
|
+
const observer = new IntersectionObserver((entries) => {
|
|
82
|
+
entries.forEach(entry => {
|
|
83
|
+
console.log(entry.intersectionRatio);
|
|
84
|
+
//this.$overlay.classList.toggle('no-offset', entry.intersectionRatio < 1);
|
|
85
|
+
// entry.isIntersecting will be false if it is entirely outside the viewport
|
|
86
|
+
if (!entry.isIntersecting) {
|
|
87
|
+
console.log('Element is completely outside the viewport');
|
|
88
|
+
} else {
|
|
89
|
+
console.log('Element is at least partially inside the viewport');
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}, {
|
|
93
|
+
root: null, // Defaults to the browser viewport
|
|
94
|
+
threshold: 1 // Triggers as soon as even 1 pixel enters or leaves
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
observer.observe(this.$overlay);
|
|
80
98
|
}
|
|
81
99
|
|
|
82
100
|
#toggle(e: ToggleEvent) {
|
|
@@ -99,4 +117,4 @@ export default class PgOverlaySelectMenu extends PgOverlay {
|
|
|
99
117
|
this.close({ value: null });
|
|
100
118
|
this.source?.focus();
|
|
101
119
|
}
|
|
102
|
-
}
|
|
120
|
+
}
|
package/pg/tree/README.md
CHANGED
|
@@ -22,6 +22,7 @@ Each item in the `items` array follows this structure:
|
|
|
22
22
|
{
|
|
23
23
|
label: string;
|
|
24
24
|
icon: { path: string };
|
|
25
|
+
disableRename: boolean;
|
|
25
26
|
isFolder: boolean; // true enables the drop-onto zone and .items CSS class
|
|
26
27
|
expanded?: boolean; // folders only
|
|
27
28
|
items?: any[]; // child items (folders only)
|
|
@@ -22,14 +22,15 @@ function createFolder(label, expanded = true, items: any[] = []) {
|
|
|
22
22
|
isFolder: true,
|
|
23
23
|
actions: [{
|
|
24
24
|
type: PgTreeButtonIcon,
|
|
25
|
+
label: 'Show/Hide',
|
|
25
26
|
icon: IconEye,
|
|
26
27
|
enabled: true
|
|
27
28
|
},
|
|
28
29
|
{
|
|
29
30
|
type: PgTreeButtonIcon,
|
|
30
|
-
label: '
|
|
31
|
+
label: 'Lock/Unlock',
|
|
31
32
|
icon: IconUnlock,
|
|
32
|
-
enabled:
|
|
33
|
+
enabled: true
|
|
33
34
|
}],
|
|
34
35
|
items
|
|
35
36
|
};
|
|
@@ -43,12 +44,13 @@ function createItem(label) {
|
|
|
43
44
|
label,
|
|
44
45
|
actions: [{
|
|
45
46
|
type: PgTreeButtonIcon,
|
|
47
|
+
label: 'Show/Hide',
|
|
46
48
|
icon: IconEye,
|
|
47
49
|
enabled: true
|
|
48
50
|
},
|
|
49
51
|
{
|
|
50
52
|
type: PgTreeButtonIcon,
|
|
51
|
-
label: '
|
|
53
|
+
label: 'Lock/Unlock',
|
|
52
54
|
icon: IconUnlock,
|
|
53
55
|
enabled: false
|
|
54
56
|
}]
|
|
@@ -89,10 +91,8 @@ export default class XPgTreeBasic extends HTMLElement {
|
|
|
89
91
|
action.enabled = true;
|
|
90
92
|
}
|
|
91
93
|
});
|
|
92
|
-
this.$tree.addEventListener('move', (
|
|
93
|
-
this
|
|
94
|
-
item.move(e.detail.item, e.detail.position);
|
|
95
|
-
});
|
|
94
|
+
this.$tree.addEventListener('move', (_e: any) => {
|
|
95
|
+
// Tree handles all data manipulation internally; this is a notification only
|
|
96
96
|
});
|
|
97
97
|
this.$tree.addEventListener('rename', (e: any) => {
|
|
98
98
|
const { item, label } = e.detail;
|
package/pg/treeItem/treeItem.ts
CHANGED
|
@@ -19,6 +19,7 @@ export default class PgTreeItem extends HTMLElement {
|
|
|
19
19
|
@Prop() label: string = '';
|
|
20
20
|
@Prop() selected: boolean = false;
|
|
21
21
|
@Prop() expanded: boolean = false;
|
|
22
|
+
@Prop() disableRename: boolean = false;
|
|
22
23
|
@Prop() isFolder: boolean = false;
|
|
23
24
|
@Prop() icon: { path: string } = { path: noIcon };
|
|
24
25
|
@Prop() actions: any[] = [];
|
|
@@ -179,7 +180,7 @@ export default class PgTreeItem extends HTMLElement {
|
|
|
179
180
|
}
|
|
180
181
|
|
|
181
182
|
#handleKeyDownLabel(e: KeyboardEvent) {
|
|
182
|
-
if (e.key === 'Enter') {
|
|
183
|
+
if (e.key === 'Enter' && !this.disableRename) {
|
|
183
184
|
this.#enableRename();
|
|
184
185
|
e.preventDefault();
|
|
185
186
|
} else if (e.key === 'ArrowUp') {
|
|
@@ -270,6 +271,7 @@ export default class PgTreeItem extends HTMLElement {
|
|
|
270
271
|
#ignoreNextClick = false;
|
|
271
272
|
#handleDoubleClick(e: MouseEvent) {
|
|
272
273
|
if (e.ctrlKey || e.shiftKey) return;
|
|
274
|
+
if (this.disableRename) return;
|
|
273
275
|
this.#enableRename();
|
|
274
276
|
e.preventDefault();
|
|
275
277
|
}
|