@pictogrammers/components 0.5.15 → 0.5.18
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/autocompleteItem/__examples__/basic/basic.html +3 -0
- package/pg/autocompleteItem/autocompleteItem.css +55 -0
- package/pg/autocompleteItem/autocompleteItem.html +1 -0
- package/pg/autocompleteItem/autocompleteItem.ts +65 -0
- package/pg/button/button.css +5 -0
- package/pg/buttonMenu/buttonMenu.ts +1 -1
- package/pg/inputCombobox/__examples__/basic/basic.ts +33 -0
- package/pg/inputCombobox/inputCombobox.ts +158 -0
- package/pg/inputCombobox/inputCombobox.ts.tmp.44252.751480cb81cc +158 -0
- package/pg/inputCombobox/inputCombobox.ts.tmp.44252.dcea847c40a8 +156 -0
- package/pg/inputPill/inputPill.html +6 -0
- package/pg/inputPill/inputPill.html.tmp.26852.84ac63f4c46f +6 -0
- package/pg/inputPill/inputPill.ts +41 -0
- package/pg/inputPills/__examples__/basic/basic.html +9 -0
- package/pg/inputPills/__examples__/basic/basic.html.tmp.26852.4771e4cad5b7 +9 -0
- package/pg/inputPills/__examples__/basic/basic.ts.tmp.26852.d8fe41df18bd +0 -0
- package/pg/inputPixelEditor/README.md +104 -69
- package/pg/inputPixelEditor/README.md.tmp.42596.658de5f0a6f5 +0 -0
- package/pg/inputPixelEditor/__examples__/basic/basic.ts +6 -5
- package/pg/inputPixelEditor/inputPixelEditor.ts +96 -24
- package/pg/inputPixelEditor/inputPixelEditor.ts.tmp.38516.96529dd82670 +1703 -0
- package/pg/inputPixelEditor/inputPixelEditor.ts.tmp.42596.48ecc3ad9c72 +1724 -0
- package/pg/inputPixelEditor/utils/bitmapToMask.ts +113 -79
- package/pg/inputPixelEditor/utils/getLassoPixels.ts +51 -0
- package/pg/inputSelect/inputSelect.ts +5 -1
- package/pg/inputText/README.md +6 -6
- package/pg/inputText/__examples__/basic/basic.ts +1 -1
- package/pg/inputText/inputText.css +2 -2
- package/pg/inputText copy/README.md +38 -0
- package/pg/inputText copy/inputText.css +41 -0
- package/pg/inputText copy/inputText.html +1 -0
- package/pg/inputText copy/inputText.spec.ts +59 -0
- package/pg/inputText copy/inputText.ts +67 -0
- package/pg/inputTextarea/README.md +39 -0
- package/pg/inputTextarea/__examples__/basic/basic.html +9 -0
- package/pg/inputTextarea/__examples__/basic/basic.ts +30 -0
- package/pg/inputTextarea/inputTextarea.ts +121 -0
- package/pg/inputTextarea copy/README.md +39 -0
- package/pg/inputTextarea copy/inputTextarea.css +41 -0
- package/pg/inputTextarea copy/inputTextarea.html +1 -0
- package/pg/inputTextarea copy/inputTextarea.spec.ts +59 -0
- package/pg/inputTextarea copy/inputTextarea.ts +79 -0
- package/pg/inputTextareaAutocomplete/README.md +70 -0
- package/pg/inputTextareaAutocomplete/__examples__/basic/basic.ts +67 -0
- package/pg/inputTextareaAutocomplete/inputTextareaAutocomplete.ts +267 -0
- package/pg/node/README.md +35 -0
- package/pg/node/__examples__/basic/basic.ts +34 -0
- package/pg/node/index.ts +3 -0
- package/pg/node/node.css +53 -0
- package/pg/node/node.html +5 -0
- package/pg/node/node.ts +270 -0
- package/pg/nodeEditorLink/nodeEditorLink.css +69 -0
- package/pg/nodeEditorLink/nodeEditorLink.css.tmp.1408.09e961fd0dcc +69 -0
- package/pg/nodeEditorLink/nodeEditorLink.html +9 -0
- package/pg/nodeEditorNumber/nodeEditorNumber.css +59 -0
- package/pg/nodeEditorNumber/nodeEditorNumber.ts +57 -0
- package/pg/nodeEditorRange/nodeEditorRange.css +23 -0
- package/pg/nodeEditorRange/nodeEditorRange.ts +69 -0
- package/pg/nodeEditorText/__examples__/basic/basic.html +9 -0
- package/pg/nodeEditorText/__examples__/basic/basic.ts +30 -0
- package/pg/nodeEditorText/nodeEditorText.css +17 -0
- package/pg/nodeEditorText/nodeEditorText.html +4 -0
- package/pg/nodeEditorText/nodeEditorText.ts +57 -0
- package/pg/nodeEditorText copy/README.md +35 -0
- package/pg/nodeEditorText copy/nodeEditorText.css +49 -0
- package/pg/nodeEditorText copy/nodeEditorText.html +4 -0
- package/pg/nodeEditorText copy/nodeEditorText.ts +50 -0
- package/pg/nodeEditorTextArray/nodeEditorTextArray.css +68 -0
- package/pg/nodeEditorTextArray/nodeEditorTextArray.html +9 -0
- package/pg/nodeEditorTextArray/nodeEditorTextArray.ts +162 -0
- package/pg/nodeEntry/nodeEntry.css +61 -0
- package/pg/nodeEntry/nodeEntry.ts +164 -0
- package/pg/nodeEntry/nodeEntry.ts.tmp.28724.dda67762bca5 +164 -0
- package/pg/nodeInputText/nodeInputText.css +75 -0
- package/pg/nodeInputText/nodeInputText.html +4 -0
- package/pg/nodeInputText/nodeInputText.ts +105 -0
- package/pg/nodeOutput/nodeOutput.css +32 -0
- package/pg/nodeResize/README.md +3 -0
- package/pg/nodeResize/THINKING.md +81 -0
- package/pg/nodeResize/THINKING.md.tmp.24812.99a70ba81062 +81 -0
- package/pg/nodeResize/basic/basic.ts +17 -0
- package/pg/nodeResize/dragUtil.ts +105 -0
- package/pg/nodeResize/dragUtil.ts.tmp.34372.787464c73309 +75 -0
- package/pg/nodeResize/nodeResize.css +187 -0
- package/pg/nodeResize/nodeResize.html +9 -0
- package/pg/nodeResize/nodeResize.ts +156 -0
- package/pg/nodes/README.md +177 -0
- package/pg/nodes/__examples__/basic/basic.html +33 -0
- package/pg/nodes/__examples__/basic/basic.ts +716 -0
- package/pg/nodes/__examples__/basic/exampleNodes.ts +129 -0
- package/pg/nodes/__examples__/basic/exampleScripts.ts +129 -0
- package/pg/nodes/__examples__/basic/keystore.ts +38 -0
- package/pg/nodes/executeInSandbox.ts +32 -0
- package/pg/nodes/index.ts +3 -0
- package/pg/nodes/nodes.css +69 -0
- package/pg/nodes/nodes.html +6 -0
- package/pg/nodes/nodes.ts +1240 -0
- package/pg/nodes copy/nodes.css +41 -0
- package/pg/nodes copy/nodes.html +1 -0
- package/pg/overlayContextMenu/__examples__/filter/filter.ts +87 -0
- package/pg/overlayContextMenu/overlayContextMenu.css +14 -0
- package/pg/overlayContextMenu/overlayContextMenu.html +4 -3
- package/pg/overlayContextMenu/overlayContextMenu.ts +6 -2
- package/pg/overlayContextMenu/overlayContextMenu.ts.tmp.37088.530ff1c0a321 +0 -0
- package/pg/overlayMenu/overlayMenu.css +3 -1
- package/pg/overlayMenu/overlayMenu.ts +11 -2
- package/pg/overlayMenu/overlayMenu.ts.tmp.44252.9bb22fd59ede +80 -0
- package/pg/overlaySelectMenu/overlaySelectMenu.css +2 -0
- package/pg/overlaySubMenu/overlaySubMenu.css +8 -6
- package/pg/poco/README.md +22 -0
- package/pg/poco/__examples__/basic/basic.html +11 -0
- package/pg/poco/__examples__/basic/basic.ts +95 -0
- package/pg/poco/__examples__/basic/examples.ts +258 -0
- package/pg/poco/__examples__/basic/myFont.ts +1300 -0
- package/pg/poco/index.ts +3 -0
- package/pg/poco/mockPoco.ts +640 -0
- package/pg/poco/poco.css +7 -0
- package/pg/poco/poco.html +1 -0
- package/pg/poco/poco.ts +75 -0
- package/pg/popover/README.md +47 -0
- package/pg/popover/__examples__/basic/basic.html +3 -0
- package/pg/popover/popover.html +3 -0
- package/pg/popover/popover.ts +44 -0
- package/pg/popoverAutocomplete/__examples__/basic/basic.html +4 -0
- package/pg/popoverAutocomplete/__examples__/basic/basic.ts +46 -0
- package/pg/popoverAutocomplete/popoverAutocomplete.css +25 -0
- package/pg/popoverAutocomplete/popoverAutocomplete.html +1 -0
- package/pg/popoverAutocomplete/popoverAutocomplete.ts +134 -0
- package/pg/popoverAutocompleteItem/popoverAutocompleteItem.css +69 -0
- package/pg/popoverAutocompleteItem/popoverAutocompleteItem.ts +90 -0
- package/pg/table/__examples__/basic/basic.ts +1 -0
- package/pg/tableCellFileLocal/tableCellFileLocal.html +3 -0
- package/pg/tableCellFileLocal/tableCellFileLocal.html.tmp.20484.68ee2e30710e +3 -0
- package/pg/tableCellFileLocal/tableCellFileLocal.ts +44 -0
- package/pg/tableCellSelect/tableCellSelect.html +3 -0
- package/pg/tableCellSelect/tableCellSelect.html.tmp.41808.99c1a6f14340 +3 -0
- package/pg/tableCellSelect/tableCellSelect.ts +51 -0
- package/pg/tree/__examples__/basic/basic.ts +12 -5
- package/pg/tree/tree.html +1 -1
- package/pg/tree/tree.ts +385 -227
- package/pg/tree/tree.ts.tmp.38376.07d376a04f41 +343 -0
- package/pg/tree/tree.ts.tmp.38376.2a04599ade9f +361 -0
- package/pg/tree/tree.ts.tmp.38376.6cb2c663d476 +0 -0
- package/pg/tree/tree.ts.tmp.38376.ac5af1aff2b0 +393 -0
- package/pg/tree/tree.ts.tmp.38376.ac9acfc4f95f +324 -0
- package/pg/treeItem/treeItem.ts +536 -558
- package/pg/treeItem/treeItem.ts.tmp.38376.145d3121d648 +530 -0
- package/pg/treeItem/treeItem.ts.tmp.38376.b5224d48e5c0 +0 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Component, Prop, Part } from '@pictogrammers/element';
|
|
2
|
+
|
|
3
|
+
import template from './inputPill.html';
|
|
4
|
+
import style from './inputPill.css';
|
|
5
|
+
|
|
6
|
+
@Component({
|
|
7
|
+
selector: 'pg-input-pill',
|
|
8
|
+
style,
|
|
9
|
+
template,
|
|
10
|
+
})
|
|
11
|
+
export default class PgInputPill extends HTMLElement {
|
|
12
|
+
@Prop() label: string = '';
|
|
13
|
+
@Prop() value: string = '';
|
|
14
|
+
|
|
15
|
+
@Part() $label: HTMLSpanElement;
|
|
16
|
+
@Part() $remove: HTMLButtonElement;
|
|
17
|
+
|
|
18
|
+
#initialized: boolean = false;
|
|
19
|
+
|
|
20
|
+
connectedCallback() {
|
|
21
|
+
// Reconnects re-invoke connectedCallback; guard against duplicate listeners.
|
|
22
|
+
if (this.#initialized) return;
|
|
23
|
+
this.#initialized = true;
|
|
24
|
+
this.$remove.addEventListener('click', () => {
|
|
25
|
+
this.dispatchEvent(
|
|
26
|
+
new CustomEvent('remove', {
|
|
27
|
+
detail: {
|
|
28
|
+
value: this.value,
|
|
29
|
+
label: this.label,
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
render(changes) {
|
|
37
|
+
if (changes.label) {
|
|
38
|
+
this.$label.textContent = this.label;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
File without changes
|
|
@@ -16,20 +16,23 @@ import PgInputPixelEditor, {
|
|
|
16
16
|
|
|
17
17
|
## Attributes
|
|
18
18
|
|
|
19
|
-
| Attributes
|
|
20
|
-
|
|
|
21
|
-
| `
|
|
22
|
-
| `
|
|
23
|
-
| `
|
|
24
|
-
| `
|
|
25
|
-
| `
|
|
19
|
+
| Attributes | Tested | Description |
|
|
20
|
+
| ------------- | -------- | ----------- |
|
|
21
|
+
| `width` | | Pixel width. Default `10` |
|
|
22
|
+
| `height` | | Pixel height. Default `10` |
|
|
23
|
+
| `size` | | Pixel size, minimum value `4`. Default `10` |
|
|
24
|
+
| `gridSize` | | Grid spacing between cells. Default `1` (changing after init does not currently re-render) |
|
|
25
|
+
| `transparent` | | Show checkerboard background. Default `false` |
|
|
26
|
+
| `placeholder` | | Placeholder text (not yet implemented) |
|
|
26
27
|
|
|
27
28
|
## Events
|
|
28
29
|
|
|
29
|
-
| Events
|
|
30
|
-
|
|
|
31
|
-
| `change`
|
|
32
|
-
| `
|
|
30
|
+
| Events | Tested | Description |
|
|
31
|
+
| ------------- | -------- | ----------- |
|
|
32
|
+
| `change` | | `{ detail: { export } }` — debounced, fires 1 second after the last edit |
|
|
33
|
+
| `selectlayer` | | `{ detail: { color, index } }` — fired in cursor input mode |
|
|
34
|
+
| `debug` | | `{ detail: { x, y, width, height, canvas, context, baseLayer, editLayer, noEditLayer, previewLayer } }` — internal layers for debugging previews |
|
|
35
|
+
| `reference` | | `{ detail: { id, callback } }` (not yet implemented) |
|
|
33
36
|
|
|
34
37
|
```typescript
|
|
35
38
|
this.$input.addEventListener('reference', (e: any) => {
|
|
@@ -46,59 +49,71 @@ See usage for each method below.
|
|
|
46
49
|
| Method | Tested | Description |
|
|
47
50
|
| ---------- | -------- | ----------- |
|
|
48
51
|
| `getJson(options)` | - | Get JSON file. |
|
|
49
|
-
| `getData()` | - | Get layer data. |
|
|
50
|
-
| `setData()` | - | Set layer data. |
|
|
51
52
|
| `setJson(json)` | - | Set JSON file. |
|
|
53
|
+
| `getData()` | - | Get layer data as paths by color. |
|
|
54
|
+
| `setData(data)` | - | Set layer data from paths. |
|
|
52
55
|
| `reset()` | - | Reset canvas and data. |
|
|
56
|
+
| `clear()` | - | Clear all pixel data (deprecated, will be removed). |
|
|
57
|
+
| `applyTemplate(grid)` | - | Replace all data with a single-layer 2d color-index grid. |
|
|
53
58
|
| `getExport()` | - | 2d array of pixel color indexes. Already calculated. |
|
|
54
|
-
| `
|
|
55
|
-
| `
|
|
59
|
+
| `getExportPath()` | - | SVG path string(s) for the export grid. |
|
|
60
|
+
| `getExportLayerIndexes()` | - | Indexes of layers flagged for export. |
|
|
61
|
+
| `getExportCanvas(options)` | - | Get new prepopulated HTML canvas, ideal for advanced export screens. |
|
|
56
62
|
| `await getExportPng(options, meta)` | - | Get png image with optional metadata. |
|
|
57
|
-
| `
|
|
58
|
-
| `
|
|
59
|
-
| `
|
|
60
|
-
| `
|
|
63
|
+
| `await getSelectionPng(options, meta)` | - | Get png of the current selection bounding box. Requires an active selection. |
|
|
64
|
+
| `await copyPngToClipboard(blob, data)` | - | Copy a png blob and JSON data to the clipboard. |
|
|
65
|
+
| `undo()` | - | Undo (partial — history is not yet recorded). |
|
|
66
|
+
| `hasUndo()` | - | Has undo. |
|
|
67
|
+
| `hasRedo()` | - | Has redo. |
|
|
68
|
+
| `redo()` | - | Redo (not yet implemented). |
|
|
61
69
|
| `getHistory()` | - | History list. |
|
|
62
|
-
| `
|
|
63
|
-
| `
|
|
64
|
-
| `
|
|
65
|
-
| `
|
|
66
|
-
| `
|
|
67
|
-
| `
|
|
68
|
-
| `
|
|
69
|
-
| `
|
|
70
|
-
| `
|
|
71
|
-
| `
|
|
72
|
-
| `
|
|
73
|
-
| `
|
|
74
|
-
| `
|
|
70
|
+
| `clearHistory()` | - | Clear undo/redo history. |
|
|
71
|
+
| `inputModeCursor()` | - | Input Mode Cursor — click to select layer. |
|
|
72
|
+
| `inputModeSelectRectangle()` | - | Input Mode Select Rectangle. |
|
|
73
|
+
| `inputModeSelectEllipse()` | - | Input Mode Select Ellipse. |
|
|
74
|
+
| `inputModeSelectLasso()` | - | Input Mode Lasso. |
|
|
75
|
+
| `inputModeSelectMagicWand()` | - | Input Mode Magic Wand. |
|
|
76
|
+
| `inputModePixel()` | - | Input Mode Pixel. |
|
|
77
|
+
| `inputModePixelSize(size)` | - | Set pixel brush size (not yet implemented). |
|
|
78
|
+
| `inputModeStamp(stamp)` | - | Input Mode Stamp. Accepts a `[x, y][]` offset array; a hover preview is shown. |
|
|
79
|
+
| `inputModeLine()` | - | Input Mode Line. |
|
|
80
|
+
| `inputModeRectangle()` | - | Input Mode Rectangle. |
|
|
81
|
+
| `inputModeRectangleOutline()` | - | Input Mode Rectangle Outline. |
|
|
82
|
+
| `inputModeEllipse()` | - | Input Mode Ellipse. |
|
|
83
|
+
| `inputModeEllipseOutline()` | - | Input Mode Ellipse Outline. |
|
|
84
|
+
| `selectLayers(indexes)` | - | Select one or more layers by index array. |
|
|
85
|
+
| `getLayers()` | - | Get layer index array. |
|
|
75
86
|
| `addLayer(option)` | - | Add layer. `name`, `type` required. |
|
|
76
|
-
| `removeLayer(index)` | - | Remove layer. |
|
|
77
|
-
| `flattenLayers(layerIndexes)` | - | Flatten layers. |
|
|
78
|
-
| `getColor()` | - | Get
|
|
79
|
-
| `getColors()` | - | Get colors. |
|
|
80
|
-
| `
|
|
81
|
-
| `
|
|
82
|
-
| `
|
|
83
|
-
| `
|
|
84
|
-
| `
|
|
85
|
-
| `
|
|
86
|
-
| `
|
|
87
|
-
| `
|
|
88
|
-
| `
|
|
89
|
-
| `
|
|
90
|
-
| `
|
|
91
|
-
| `
|
|
92
|
-
| `
|
|
93
|
-
| `
|
|
94
|
-
| `
|
|
95
|
-
| `
|
|
96
|
-
| `
|
|
97
|
-
| `
|
|
98
|
-
| `
|
|
99
|
-
| `
|
|
100
|
-
| `
|
|
101
|
-
| `
|
|
87
|
+
| `removeLayer(index)` | - | Remove layer by index. |
|
|
88
|
+
| `flattenLayers(layerIndexes)` | - | Flatten layers (not yet implemented). |
|
|
89
|
+
| `getColor(index)` | - | Get color by index. |
|
|
90
|
+
| `getColors()` | - | Get all colors. |
|
|
91
|
+
| `getColorAt(x, y)` | - | Get the export color index at a coordinate. |
|
|
92
|
+
| `getLayerAt(x, y)` | - | Get the top-most layer index with a color at a coordinate, or `null`. |
|
|
93
|
+
| `getLayerColorIndexes(layerIndex?)` | - | Get unique color indexes on selected (or given) layer. |
|
|
94
|
+
| `getLayerPaths()` | - | Get SVG path strings per color per layer. |
|
|
95
|
+
| `selectColor(index)` | - | Set the active draw color. |
|
|
96
|
+
| `setColor(index, r, g, b, a)` | - | Update a color by index. |
|
|
97
|
+
| `addColor(r, g, b, a)` | - | Append a color. |
|
|
98
|
+
| `removeColor(index)` | - | Remove a color by index. |
|
|
99
|
+
| `moveColor(startIndex, endIndex)` | - | Move a color (not yet implemented). |
|
|
100
|
+
| `mergeColor(fromIndex, toIndex)` | - | Merge one color into another (not yet implemented). |
|
|
101
|
+
| `rotateClockwise()` | - | Rotate active layer 90° clockwise. |
|
|
102
|
+
| `rotateCounterclockwise()` | - | Rotate active layer 90° counter-clockwise. |
|
|
103
|
+
| `move(x, y)` | - | Translate active layer by x/y pixels. |
|
|
104
|
+
| `flipHorizontal()` | - | Flip active layer horizontally. |
|
|
105
|
+
| `flipVertical()` | - | Flip active layer vertically. |
|
|
106
|
+
| `invert()` | - | Invert active layer (2-color only). |
|
|
107
|
+
| `outline(include?)` | - | Draw outline around active layer with selected color. |
|
|
108
|
+
| `glow(include?)` | - | Draw glow (outer outline) with selected color. |
|
|
109
|
+
| `applyGuides()` | - | Draw guide lines onto the base layer. |
|
|
110
|
+
| `clearGuides()` | - | Clear guide lines (not yet implemented). |
|
|
111
|
+
| `drawGrid(grid, layer?)` | - | Draw a full 2d color-index grid onto a layer. |
|
|
112
|
+
| `drawPixel(x, y, color, layer?)` | - | Draw a single pixel. |
|
|
113
|
+
| `hasSelection()` | - | Returns true when pixels are selected. |
|
|
114
|
+
| `getSelection()` | - | Get selection pixels (not yet implemented). |
|
|
115
|
+
| `clearSelection()` | - | Deselect all pixels. |
|
|
116
|
+
| `moveSelection(x, y)` | - | Translate selection and its pixels by x/y. |
|
|
102
117
|
|
|
103
118
|
### `getJson(options)` Method
|
|
104
119
|
|
|
@@ -107,8 +122,8 @@ The `getJson` method allows getting the JSON representation of the current edito
|
|
|
107
122
|
```typescript
|
|
108
123
|
@Part() $editor: PgInputPixelEditor;
|
|
109
124
|
|
|
110
|
-
handleSave() {
|
|
111
|
-
const json = await this.$editor.
|
|
125
|
+
async handleSave() {
|
|
126
|
+
const json = await this.$editor.getJson({
|
|
112
127
|
// Include history
|
|
113
128
|
history: true,
|
|
114
129
|
});
|
|
@@ -118,16 +133,13 @@ handleSave() {
|
|
|
118
133
|
|
|
119
134
|
### `setJson(json)` Method
|
|
120
135
|
|
|
121
|
-
The
|
|
136
|
+
The `setJson` method loads a previously saved JSON file into the editor.
|
|
122
137
|
|
|
123
138
|
```typescript
|
|
124
139
|
@Part() $editor: PgInputPixelEditor;
|
|
125
140
|
|
|
126
|
-
handleOpen() {
|
|
127
|
-
|
|
128
|
-
if (error) {
|
|
129
|
-
throw new Error(error.message);
|
|
130
|
-
}
|
|
141
|
+
async handleOpen(json) {
|
|
142
|
+
await this.$editor.setJson(json);
|
|
131
143
|
}
|
|
132
144
|
```
|
|
133
145
|
|
|
@@ -135,6 +147,14 @@ handleOpen() {
|
|
|
135
147
|
|
|
136
148
|
### `change`
|
|
137
149
|
|
|
150
|
+
Debounced — fires 1 second after the last edit. The detail contains the cached export grid.
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
this.$input.addEventListener('change', (e: any) => {
|
|
154
|
+
const { export: grid } = e.detail;
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
138
158
|
### `selectLayer`
|
|
139
159
|
|
|
140
160
|
Using `InputMode.Cursor` individual pixels can be clicked on to select the layer. Note, selecting a layer does not automatically select it. The event must be handled.
|
|
@@ -142,10 +162,25 @@ Using `InputMode.Cursor` individual pixels can be clicked on to select the layer
|
|
|
142
162
|
```typescript
|
|
143
163
|
this.$input.addEventListener('selectlayer', (e: any) => {
|
|
144
164
|
const { color, index } = e.detail;
|
|
145
|
-
this.selectLayers([index]);
|
|
165
|
+
this.$input.selectLayers([index]);
|
|
146
166
|
})
|
|
147
167
|
```
|
|
148
168
|
|
|
169
|
+
## Keyboard Shortcuts
|
|
170
|
+
|
|
171
|
+
The wrapper is focusable (`tabindex="0"`); shortcuts apply while the editor has focus.
|
|
172
|
+
|
|
173
|
+
| Keys | Description |
|
|
174
|
+
| ---- | ----------- |
|
|
175
|
+
| `Escape` | Clear the current selection. |
|
|
176
|
+
| `Delete` | Delete the pixels in the current selection. |
|
|
177
|
+
| `Arrow Keys` | Move the current selection by 1 pixel. |
|
|
178
|
+
| `Shift + Arrow Keys` | Move the current selection by 10 pixels. |
|
|
179
|
+
| `Ctrl + C` | Copy the selection (or the full export when nothing is selected) to the clipboard as a PNG. |
|
|
180
|
+
| `Ctrl + V` | Paste clipboard PNG / easel JSON (work in progress — currently logs metadata only). |
|
|
181
|
+
| `Ctrl + Z` | Undo. |
|
|
182
|
+
| `Ctrl + Shift + Z` / `Ctrl + Y` | Redo. |
|
|
183
|
+
|
|
149
184
|
## JSON Format
|
|
150
185
|
|
|
151
186
|
- `width` - Image width.
|
|
@@ -360,7 +395,7 @@ To create a repeating 10 pixel grid with an offset `5` and limit `10`. The use c
|
|
|
360
395
|
|
|
361
396
|
Exports by default include the entire canvas, but can be resized to a specific pixel size or grid. The `x`, `y`, `width`, and `height` are optional.
|
|
362
397
|
|
|
363
|
-
Use `getExport(
|
|
398
|
+
Use `getExport()` to get the cached 2d color-index grid. Use `getExportCanvas(options)` with `x`, `y`, `width`, `height` to crop to a region — ideal for export previews.
|
|
364
399
|
|
|
365
400
|
```json
|
|
366
401
|
{
|
|
@@ -388,7 +423,7 @@ Use `getExport(id)` with a `id` value to get the data grid. This is ideal for ex
|
|
|
388
423
|
|
|
389
424
|
The history list contains every change made to the canvas. All feature insert an entry into history. Pens and pattern tools will group as to cut down history entries.
|
|
390
425
|
|
|
391
|
-
Note: Calling the `draw*()` commands
|
|
426
|
+
Note: Calling the `draw*()` commands programmatically will group into a single history item.
|
|
392
427
|
|
|
393
428
|
- Undo a change
|
|
394
429
|
- Redo an undo
|
|
File without changes
|
|
@@ -253,7 +253,7 @@ export default class XPgInputPixelEditorBasic extends HTMLElement {
|
|
|
253
253
|
}
|
|
254
254
|
})
|
|
255
255
|
);
|
|
256
|
-
this.$input.addLayer();
|
|
256
|
+
this.$input.addLayer({ name: 'layer 1', type: 'pixel' });
|
|
257
257
|
});
|
|
258
258
|
this.$addLayerReference.addEventListener('click', () => {
|
|
259
259
|
this.$layers.data.push(
|
|
@@ -268,7 +268,7 @@ export default class XPgInputPixelEditorBasic extends HTMLElement {
|
|
|
268
268
|
}
|
|
269
269
|
})
|
|
270
270
|
);
|
|
271
|
-
this.$input.addLayer();
|
|
271
|
+
this.$input.addLayer({ name: 'layer 1', type: 'pixel' });
|
|
272
272
|
});
|
|
273
273
|
// Colors
|
|
274
274
|
this.$colors.columns = [{
|
|
@@ -346,9 +346,9 @@ export default class XPgInputPixelEditorBasic extends HTMLElement {
|
|
|
346
346
|
});
|
|
347
347
|
this.$addColor.addEventListener('click', () => {
|
|
348
348
|
this.$colors.data.push(createTableItem({
|
|
349
|
-
r:
|
|
350
|
-
g:
|
|
351
|
-
b:
|
|
349
|
+
r: 0,
|
|
350
|
+
g: 0,
|
|
351
|
+
b: 0,
|
|
352
352
|
a: 1,
|
|
353
353
|
selected: false,
|
|
354
354
|
select: {
|
|
@@ -361,6 +361,7 @@ export default class XPgInputPixelEditorBasic extends HTMLElement {
|
|
|
361
361
|
icon: IconTrash
|
|
362
362
|
}
|
|
363
363
|
}));
|
|
364
|
+
this.$input.addColor(0, 0, 0, 1);
|
|
364
365
|
});
|
|
365
366
|
}
|
|
366
367
|
|
|
@@ -9,6 +9,7 @@ import getEllipseOutlinePixels from './utils/getEllipseOutlinePixels';
|
|
|
9
9
|
import { WHITE, Pixel } from './utils/constants';
|
|
10
10
|
import getLinePixels from './utils/getLinePixels';
|
|
11
11
|
import getRectanglePixels from './utils/getRectanglePixels';
|
|
12
|
+
import getLassoPixels from './utils/getLassoPixels';
|
|
12
13
|
import getRectangleOutlinePixels from './utils/getRectangleOutlinePixels';
|
|
13
14
|
import fillGrid from './utils/fillGrid';
|
|
14
15
|
import iterateGrid from './utils/interateGrid';
|
|
@@ -96,6 +97,16 @@ interface Export {
|
|
|
96
97
|
height?: number
|
|
97
98
|
}
|
|
98
99
|
|
|
100
|
+
interface AddLayerOptions {
|
|
101
|
+
name: string
|
|
102
|
+
type: Layer['type']
|
|
103
|
+
exclude?: boolean
|
|
104
|
+
locked?: boolean
|
|
105
|
+
hidden?: boolean
|
|
106
|
+
opacity?: number
|
|
107
|
+
data?: any[]
|
|
108
|
+
}
|
|
109
|
+
|
|
99
110
|
interface GridItem {
|
|
100
111
|
value: number
|
|
101
112
|
color: number
|
|
@@ -137,6 +148,11 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
137
148
|
#startColor: number = -1;
|
|
138
149
|
#startX: number = -1;
|
|
139
150
|
#startY: number = -1;
|
|
151
|
+
#startClientX: number = -1;
|
|
152
|
+
#startClientY: number = -1;
|
|
153
|
+
#lassoPath: [number, number][] = [];
|
|
154
|
+
#lassoOutlinePixels: Set<string> = new Set();
|
|
155
|
+
#lassoOutlineList: { x: number; y: number }[] = [];
|
|
140
156
|
#x: number = -1;
|
|
141
157
|
#y: number = -1;
|
|
142
158
|
#layer: number[] = [0];
|
|
@@ -338,19 +354,22 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
338
354
|
#setSelectionPreview(pixels: Pixel[]) {
|
|
339
355
|
// Undo previous selection
|
|
340
356
|
this.#previousPreview.forEach(({x, y}) => {
|
|
357
|
+
if (x < 0 || x >= this.width || y < 0 || y >= this.height) { return; }
|
|
341
358
|
this.#selectionPreview[y][x] = 0;
|
|
342
359
|
});
|
|
360
|
+
this.#previousPreview = pixels;
|
|
343
361
|
pixels.forEach(({ x, y }) => {
|
|
344
|
-
this
|
|
362
|
+
if (x < 0 || x >= this.width || y < 0 || y >= this.height) { return; }
|
|
345
363
|
this.#selectionPreview[y][x] = 1;
|
|
346
364
|
});
|
|
347
365
|
this.$selectionPathPreview.classList.toggle('hide', false);
|
|
348
|
-
this.$selectionPathPreview.setAttribute('d', bitmaskToPath(this.#selectionPreview, { scale: this.size }));
|
|
366
|
+
this.$selectionPathPreview.setAttribute('d', bitmaskToPath(this.#selectionPreview, { scale: this.size })[0]);
|
|
349
367
|
}
|
|
350
368
|
|
|
351
369
|
#clearSelectionPreview() {
|
|
352
370
|
this.$selectionPathPreview.classList.toggle('hide', true);
|
|
353
371
|
this.#previousPreview.forEach(({x, y}) => {
|
|
372
|
+
if (x < 0 || x >= this.width || y < 0 || y >= this.height) { return; }
|
|
354
373
|
this.#selectionPreview[y][x] = 0;
|
|
355
374
|
});
|
|
356
375
|
}
|
|
@@ -375,6 +394,7 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
375
394
|
}
|
|
376
395
|
|
|
377
396
|
#setSelectionPixel(x: number, y: number) {
|
|
397
|
+
if (x < 0 || x >= this.width || y < 0 || y >= this.height) { return; }
|
|
378
398
|
this.#selectionPixels.set(`${x},${y}`, [x, y]);
|
|
379
399
|
this.#selection[y][x] = 1;
|
|
380
400
|
}
|
|
@@ -650,7 +670,6 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
650
670
|
}
|
|
651
671
|
|
|
652
672
|
async handleKeyDown(event: KeyboardEvent) {
|
|
653
|
-
console.log(event.shiftKey, event.ctrlKey, event.altKey, event.key);
|
|
654
673
|
this.#isShift = true;
|
|
655
674
|
switch (event.key) {
|
|
656
675
|
case ' ':
|
|
@@ -757,6 +776,8 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
757
776
|
this.#startColor = this.#data[this.#layer[0]][newY][newX];
|
|
758
777
|
this.#startX = newX;
|
|
759
778
|
this.#startY = newY;
|
|
779
|
+
this.#startClientX = event.clientX;
|
|
780
|
+
this.#startClientY = event.clientY;
|
|
760
781
|
this.#x = newX;
|
|
761
782
|
this.#y = newY;
|
|
762
783
|
const color = event.buttons === 32 ? 0 : this.#color;
|
|
@@ -771,6 +792,11 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
771
792
|
}
|
|
772
793
|
}));
|
|
773
794
|
break;
|
|
795
|
+
case InputMode.SelectLasso:
|
|
796
|
+
this.#lassoPath = [[newX, newY]];
|
|
797
|
+
this.#lassoOutlinePixels = new Set();
|
|
798
|
+
this.#lassoOutlineList = [];
|
|
799
|
+
break;
|
|
774
800
|
case InputMode.Pixel:
|
|
775
801
|
this.#setPixel(newX, newY, color);
|
|
776
802
|
break;
|
|
@@ -800,33 +826,44 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
800
826
|
const totalSize = this.size + this.gridSize;
|
|
801
827
|
let newX = Math.floor((event.clientX - rect.left) / totalSize);
|
|
802
828
|
let newY = Math.floor((event.clientY - rect.top) / totalSize);
|
|
803
|
-
|
|
804
|
-
|
|
829
|
+
if (newX >= this.width) { newX = this.width - 1; }
|
|
830
|
+
if (newY >= this.height) { newY = this.height - 1; }
|
|
831
|
+
if (newX < 0) { newX = 0; }
|
|
832
|
+
if (newY < 0) { newY = 0; }
|
|
805
833
|
//if (this.#startX === -1 && this.#startY === -1) {
|
|
806
834
|
// return;
|
|
807
835
|
//}
|
|
808
|
-
|
|
809
|
-
|
|
836
|
+
const isDragSelect = this.#inputMode === InputMode.SelectRectangle
|
|
837
|
+
|| this.#inputMode === InputMode.SelectEllipse
|
|
838
|
+
|| this.#inputMode === InputMode.SelectLasso;
|
|
839
|
+
// Select tools moving less than 3px act as a click to deselect,
|
|
840
|
+
// shift keeps the current selection
|
|
841
|
+
if (isDragSelect
|
|
842
|
+
&& Math.abs(event.clientX - this.#startClientX) < 3
|
|
843
|
+
&& Math.abs(event.clientY - this.#startClientY) < 3) {
|
|
844
|
+
this.#clearSelectionPreview();
|
|
845
|
+
if (!event.shiftKey) {
|
|
846
|
+
this.clearSelection();
|
|
847
|
+
}
|
|
848
|
+
} else if (newX === this.#startX && newY === this.#startY) {
|
|
810
849
|
switch (this.#inputMode) {
|
|
811
850
|
case InputMode.SelectMagicWand:
|
|
812
851
|
if (!event.shiftKey) {
|
|
813
852
|
this.clearSelection();
|
|
814
853
|
}
|
|
815
854
|
const color = this.#data[this.#layer[0]][newY][newX];
|
|
816
|
-
console.log(color);
|
|
817
855
|
const pixels = getFloodFill(this.#data[this.#layer[0]], newX, newY, [color]);
|
|
818
856
|
pixels.forEach(([x, y]) => {
|
|
819
857
|
this.#setSelectionPixel(x, y);
|
|
820
858
|
});
|
|
821
859
|
this.$selectionPathPreview.classList.toggle('hide', true);
|
|
822
860
|
this.$selectionPath.classList.toggle('hide', false);
|
|
823
|
-
this.$selectionPath.setAttribute('d', bitmaskToPath(this.#selection, { scale: this.size }));
|
|
861
|
+
this.$selectionPath.setAttribute('d', bitmaskToPath(this.#selection, { scale: this.size })[0]);
|
|
824
862
|
break;
|
|
825
863
|
case InputMode.Pixel:
|
|
826
864
|
// Same as current color
|
|
827
865
|
if (this.#startColor === this.#color) {
|
|
828
866
|
this.#setPixel(newX, newY, 0);
|
|
829
|
-
this.#data[this.#layer[0]][newY][newX] = 0;
|
|
830
867
|
}
|
|
831
868
|
break;
|
|
832
869
|
case InputMode.Stamp:
|
|
@@ -850,7 +887,7 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
850
887
|
});
|
|
851
888
|
this.$selectionPathPreview.classList.toggle('hide', true);
|
|
852
889
|
this.$selectionPath.classList.toggle('hide', false);
|
|
853
|
-
this.$selectionPath.setAttribute('d', bitmaskToPath(this.#selection, { scale: this.size }));
|
|
890
|
+
this.$selectionPath.setAttribute('d', bitmaskToPath(this.#selection, { scale: this.size })[0]);
|
|
854
891
|
break;
|
|
855
892
|
case InputMode.SelectEllipse:
|
|
856
893
|
this.#clearSelectionPreview();
|
|
@@ -862,10 +899,19 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
862
899
|
});
|
|
863
900
|
this.$selectionPathPreview.classList.toggle('hide', true);
|
|
864
901
|
this.$selectionPath.classList.toggle('hide', false);
|
|
865
|
-
this.$selectionPath.setAttribute('d', bitmaskToPath(this.#selection, { scale: this.size }));
|
|
902
|
+
this.$selectionPath.setAttribute('d', bitmaskToPath(this.#selection, { scale: this.size })[0]);
|
|
866
903
|
break;
|
|
867
904
|
case InputMode.SelectLasso:
|
|
868
|
-
|
|
905
|
+
this.#clearSelectionPreview();
|
|
906
|
+
if (!event.shiftKey) {
|
|
907
|
+
this.clearSelection();
|
|
908
|
+
}
|
|
909
|
+
getLassoPixels(this.#lassoPath).forEach(({ x, y }) => {
|
|
910
|
+
this.#setSelectionPixel(x, y);
|
|
911
|
+
});
|
|
912
|
+
this.$selectionPathPreview.classList.toggle('hide', true);
|
|
913
|
+
this.$selectionPath.classList.toggle('hide', false);
|
|
914
|
+
this.$selectionPath.setAttribute('d', bitmaskToPath(this.#selection, { scale: this.size })[0]);
|
|
869
915
|
break;
|
|
870
916
|
case InputMode.Line:
|
|
871
917
|
getLinePixels(this.#startX, this.#startY, newX, newY).forEach(({ x, y }) => {
|
|
@@ -953,6 +999,28 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
953
999
|
case InputMode.SelectRectangle:
|
|
954
1000
|
this.#setSelectionPreview(getRectanglePixels(startX, startY, lastX, lastY));
|
|
955
1001
|
break;
|
|
1002
|
+
case InputMode.SelectEllipse:
|
|
1003
|
+
this.#setSelectionPreview(getEllipsePixels(startX, startY, lastX, lastY));
|
|
1004
|
+
break;
|
|
1005
|
+
case InputMode.SelectLasso: {
|
|
1006
|
+
const [sx, sy] = this.#lassoPath[0];
|
|
1007
|
+
for (const [px, py] of points) {
|
|
1008
|
+
const [prevX, prevY] = this.#lassoPath.at(-1)!;
|
|
1009
|
+
for (const { x: lx, y: ly } of getLinePixels(prevX, prevY, px, py)) {
|
|
1010
|
+
const key = `${lx},${ly}`;
|
|
1011
|
+
if (!this.#lassoOutlinePixels.has(key)) {
|
|
1012
|
+
this.#lassoOutlinePixels.add(key);
|
|
1013
|
+
this.#lassoOutlineList.push({ x: lx, y: ly });
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
this.#lassoPath.push([px, py]);
|
|
1017
|
+
}
|
|
1018
|
+
const [cx, cy] = this.#lassoPath.at(-1)!;
|
|
1019
|
+
const closing = getLinePixels(cx, cy, sx, sy)
|
|
1020
|
+
.filter(({ x: lx, y: ly }) => !this.#lassoOutlinePixels.has(`${lx},${ly}`));
|
|
1021
|
+
this.#setSelectionPreview([...this.#lassoOutlineList, ...closing]);
|
|
1022
|
+
break;
|
|
1023
|
+
}
|
|
956
1024
|
case InputMode.Pixel:
|
|
957
1025
|
for (var point of points) {
|
|
958
1026
|
this.#setPixel(point[0], point[1], color);
|
|
@@ -1100,7 +1168,7 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
1100
1168
|
const cloned = cloneGrid(this.#data[this.#layer[0]]);
|
|
1101
1169
|
const h = cloned.length - 1;
|
|
1102
1170
|
iterateGrid(this.#data[this.#layer[0]], (x, y) => {
|
|
1103
|
-
cloned[
|
|
1171
|
+
cloned[y][x] = this.#data[this.#layer[0]][h - y][x];
|
|
1104
1172
|
});
|
|
1105
1173
|
this.#data[this.#layer[0]] = cloned;
|
|
1106
1174
|
}
|
|
@@ -1332,7 +1400,7 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
1332
1400
|
return this.#colors;
|
|
1333
1401
|
}
|
|
1334
1402
|
|
|
1335
|
-
addColor(r, g, b, a) {
|
|
1403
|
+
addColor(r: number, g: number, b: number, a: number) {
|
|
1336
1404
|
this.#colors.push([r, g, b, a]);
|
|
1337
1405
|
}
|
|
1338
1406
|
|
|
@@ -1379,7 +1447,7 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
1379
1447
|
this.#layer = indexes;
|
|
1380
1448
|
}
|
|
1381
1449
|
|
|
1382
|
-
addLayer({ name, type, ...optional }) {
|
|
1450
|
+
addLayer({ name, type, ...optional }: AddLayerOptions) {
|
|
1383
1451
|
this.#data.push(fillGrid(this.width, this.height));
|
|
1384
1452
|
this.#layers.push({
|
|
1385
1453
|
name,
|
|
@@ -1466,8 +1534,12 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
1466
1534
|
#getLayerPaths() {
|
|
1467
1535
|
return this.#data.map((layer, layerIndex) => {
|
|
1468
1536
|
const colors = this.getLayerColorIndexes(layerIndex);
|
|
1469
|
-
|
|
1470
|
-
|
|
1537
|
+
const paths = bitmaskToPath(layer, {
|
|
1538
|
+
scale: 1,
|
|
1539
|
+
include: colors.map((color) => ([color])),
|
|
1540
|
+
});
|
|
1541
|
+
return colors.map((color, i) => {
|
|
1542
|
+
return [color, paths[i]];
|
|
1471
1543
|
});
|
|
1472
1544
|
});
|
|
1473
1545
|
}
|
|
@@ -1489,8 +1561,8 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
1489
1561
|
color,
|
|
1490
1562
|
path: bitmaskToPath(this.#data[layerIndex], {
|
|
1491
1563
|
scale: 1,
|
|
1492
|
-
include: [color]
|
|
1493
|
-
}),
|
|
1564
|
+
include: [[color]]
|
|
1565
|
+
})[0],
|
|
1494
1566
|
};
|
|
1495
1567
|
});
|
|
1496
1568
|
case 'reference':
|
|
@@ -1603,8 +1675,8 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
1603
1675
|
...options,
|
|
1604
1676
|
x: minX,
|
|
1605
1677
|
y: minY,
|
|
1606
|
-
width: maxX - minX,
|
|
1607
|
-
height: maxY - minY,
|
|
1678
|
+
width: maxX - minX + 1,
|
|
1679
|
+
height: maxY - minY + 1,
|
|
1608
1680
|
}, meta);
|
|
1609
1681
|
}
|
|
1610
1682
|
|
|
@@ -1619,6 +1691,7 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
1619
1691
|
this.#selectionPixels.forEach(([currentX, currentY]) => {
|
|
1620
1692
|
const newX = currentX + x;
|
|
1621
1693
|
const newY = currentY + y;
|
|
1694
|
+
if (newX < 0 || newX >= this.width || newY < 0 || newY >= this.height) { return; }
|
|
1622
1695
|
this.#layer.forEach((layer) => {
|
|
1623
1696
|
const color = this.#data[layer][currentY][currentX];
|
|
1624
1697
|
const newColor = this.#data[layer][newY][newX];
|
|
@@ -1632,7 +1705,6 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
1632
1705
|
});
|
|
1633
1706
|
const clearSelection = diffLeftMapPixels(this.#selectionPixels, newSelection);
|
|
1634
1707
|
// Move pixels
|
|
1635
|
-
console.log(changes);
|
|
1636
1708
|
changes.forEach(([layer, newX, newY, color]) => {
|
|
1637
1709
|
this.#data[layer][newY][newX] = color;
|
|
1638
1710
|
this.#setPixel(newX, newY, color, [layer]);
|
|
@@ -1657,7 +1729,7 @@ export default class PgInputPixelEditor extends HTMLElement {
|
|
|
1657
1729
|
});
|
|
1658
1730
|
|
|
1659
1731
|
// Update render
|
|
1660
|
-
this.$selectionPath.setAttribute('d', bitmaskToPath(this.#selection, { scale: this.size }));
|
|
1732
|
+
this.$selectionPath.setAttribute('d', bitmaskToPath(this.#selection, { scale: this.size })[0]);
|
|
1661
1733
|
}
|
|
1662
1734
|
}
|
|
1663
1735
|
|